PageSourceSearch

https://www.diploma-degree.com/Projection%20-%20Responsive%20HTML5%20Template_files/main.js?v=1.1.9

js diploma-degree.com collected 2026-09-26 10:16:41 UTC 107,133 bytes, 2,868 lines download raw bytes

1var video;
2var loadedModel;
3var isModelOk = false;
4var canvasFac;
5var origModelW = 0;
6var faceDim = [300,446];
7var ratioWebcamUsefull = 0.6;
8var webcamPlaying = false;
9
10window.requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame ||
11window.webkitRequestAnimationFrame || window.msRequestAnimationFrame;
12var IMG_scale;
13
14function step(timestamp) {
15	if (!isModelOk) return;
16	if (!webcamPlaying) {
17		requestAnimationFrame(step);
18		return;
19	}
20	var ctx = canvasFac.getContext('2d')
21	var IMG_top = $('#IMG_top').val()*canvasFac.width/origModelW;
22	var IMG_left = $('#IMG_left').val()*canvasFac.width/origModelW;
23	var IMG_scale = $('#IMG_scale').val()*canvasFac.width/origModelW;
24	var IMG_rot = $('#IMG_rot').val();
25	
26	var wres,hres,wsrc,hsrc,xsrc,ysrc;
27	wres = IMG_scale;
28	hres = IMG_scale*faceDim[1]/faceDim[0];
29	
30	if (video.videoHeight/video.videoWidth>faceDim[1]/faceDim[0]) {
31		wsrc = video.videoWidth*ratioWebcamUsefull;
32		hsrc = wsrc*faceDim[1]/faceDim[0];
33	} else {
34		hsrc = video.videoHeight*ratioWebcamUsefull;
35		wsrc = hsrc/faceDim[1]*faceDim[0];
36	}
37	var angleInRadians = IMG_rot*Math.PI/180;
38
39
40	ctx.save();
41	if (angleInRadians>0)
42		ctx.translate(Math.sin(angleInRadians)*hres,0);
43	else
44		ctx.translate(0,Math.sin(-angleInRadians)*wres);
45	ctx.translate(IMG_left,IMG_top);
46	ctx.rotate(angleInRadians);
47
48	ctx.drawImage(video,video.videoWidth/2-wsrc/2,video.videoHeight/2-hsrc/2,wsrc,hsrc, 0,0, wres, hres);//-wres/2, -hres/2
49	ctx.restore();
50
51	ctx.drawImage(loadedModel,0,0,canvasFac.width,canvasFac.height);
52	
53	var testtxt = "www.face-in-hole.net";
54	ctx.font="12px Verdana";
55	ctx.fillStyle = "Black";
56	var len = ctx.measureText(testtxt);
57	//console.log(len);
58	ctx.fillText(testtxt,canvasFac.width/2-len.width/2,canvasFac.height-12-2);
59	ctx.fillStyle = "White";
60	ctx.fillText(testtxt,canvasFac.width/2-1-len.width/2,canvasFac.height-12-3);
61	
62	var ratioM = 6;
63	ctx.drawImage(video,video.videoWidth/2-video.videoWidth*ratioWebcamUsefull/2,video.videoHeight/2-video.videoHeight*ratioWebcamUsefull/2,video.videoWidth*ratioWebcamUsefull,video.videoHeight*ratioWebcamUsefull, canvasFac.width-video.videoWidth/ratioM, canvasFac.height-video.videoHeight/ratioM, video.videoWidth/ratioM, video.videoHeight/ratioM);
64	requestAnimationFrame(step);
65}
66
67function disableWebcam() {
68	try { window.stream.stop(); } catch (e) {};
69	video.srcObject = null;
70	video.remove();
71	delete video;
72	video = null;
73	
74}
75
76function enableWebcam() {
77	
78		var constraints = {
79		  audio: false,
80		  video: true
81		};
82		if (!navigator.mediaDevices) {
83			alert('You browser does not suport camera capture, please use Chrome or Firefox in order to enjoy website fully.');
84		}else 
85		navigator.mediaDevices.getUserMedia(constraints).
86		    then(function (stream) {// SUCCESS
87		    	if (!loadedModel) {
88
89						video = document.createElement('video');
90						//console.log('set video src',video);
91			  	  window.stream = stream; // make stream available to browser console
92			  	  video.autoplay = true;
93			  	  video.srcObject = stream;
94	
95			  	  //$(video).css('transform','scale(-1,1)');
96			  	  video.addEventListener('playing', function(ev) {
97			  		  //resizeWCpreview();
98			  		  webcamPlaying = true;
99			  		  //console.log('playing',video.videoWidth);
100			  	  });
101			  	  
102			  	  
103		    		loadedModel = document.createElement('img');
104		    		loadedModel.onload = function () {
105		    			var ww = $('#diplomeImg').width();
106		    			var hh = $('#diplomeImg').height();
107		    			origModelW = loadedModel.width;
108		    			//console.log('orgiModel width',origModelW);
109			    		loadedModel.width = ww;
110
111
112		    			canvasFac = document.createElement('canvas');
113		    			canvasFac.width = ww;
114		    			canvasFac.height = hh;
115		    			canvasFac.style.width = ww;
116		    			canvasFac.style.height = hh;
117		    			canvasFac.onclick = function () {
118		    					isModelOk = false;
119			    				var tmpC = document.createElement('canvas');
120			    				tmpC.width = video.videoWidth*ratioWebcamUsefull;
121			    				tmpC.height = video.videoHeight*ratioWebcamUsefull;
122			    				//tmpC.getContext('2d').drawImage(video,0,0,video.videoWidth,video.videoHeight);
123	
124			    				tmpC.getContext('2d').drawImage(video,video.videoWidth/2-tmpC.width/2,video.videoHeight/2-tmpC.height/2,video.videoWidth*ratioWebcamUsefull,video.videoHeight*ratioWebcamUsefull, 0, 0, tmpC.width, tmpC.height);
125			    				//tmpC.getContext('2d').drawImage(video,video.videoWidth/4,video.videoHeight/4,video.videoWidth/2,video.videoHeight/2, 0, 0, video.videoWidth/2, video.videoHeight/2);
126			    				
127			    				$('#b64').val(tmpC.toDataURL("image/jpeg"));
128			    			  disableWebcam() ;
129			    				$('#imgForm').submit();
130		    			}
131		    			canvasFac.className='button';
132		    			canvasFac.addEventListener('contextmenu', function (event) {
133		    				if (!window.warned) {
134		    					window.warned = true;
135		    					alert('Click on image to make snapshot.');
136		    					//alert('Use the download button in order to download in high resolution.');
137			    				event.preventDefault();
138		    				}
139		    			} );
140		    			$('#diplomeImg').after(canvasFac);
141		    			$('#diplomeImg').remove();
142		    			canvasFac.id = 'diplomeImg';
143		    			isModelOk = true;
144		    			requestAnimationFrame(step);
145		    		}
146		    		loadedModel.src= '/Deguisements/Modeles/'+$('#modeleDiplome').val()+'.png';
147		    		
148		    		
149		    	}
150			  	
151		    }).catch(function (error) {
152		  	  alert('navigator.getUserMedia error: ', error);
153		    });
154	
155}
156
157
158(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.adapter = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
159	 /* eslint-env node */
160	'use strict';
161
162	// SDP helpers.
163	var SDPUtils = {};
164
165	// Generate an alphanumeric identifier for cname or mids.
166	// TODO: use UUIDs instead? https://gist.github.com/jed/982883
167	SDPUtils.generateIdentifier = function() {
168	  return Math.random().toString(36).substr(2, 10);
169	};
170
171	// The RTCP CNAME used by all peerconnections from the same JS.
172	SDPUtils.localCName = SDPUtils.generateIdentifier();
173
174	// Splits SDP into lines, dealing with both CRLF and LF.
175	SDPUtils.splitLines = function(blob) {
176	  return blob.trim().split('\n').map(function(line) {
177	    return line.trim();
178	  });
179	};
180	// Splits SDP into sessionpart and mediasections. Ensures CRLF.
181	SDPUtils.splitSections = function(blob) {
182	  var parts = blob.split('\nm=');
183	  return parts.map(function(part, index) {
184	    return (index > 0 ? 'm=' + part : part).trim() + '\r\n';
185	  });
186	};
187
188	// Returns lines that start with a certain prefix.
189	SDPUtils.matchPrefix = function(blob, prefix) {
190	  return SDPUtils.splitLines(blob).filter(function(line) {
191	    return line.indexOf(prefix) === 0;
192	  });
193	};
194
vendor: 4,293 bytes, lines 195-330
195	// Parses an ICE candidate line. Sample input:
196	// candidate:702786350 2 udp 41819902 8.8.8.8 60769 typ relay raddr 8.8.8.8
197	// rport 55996"
198	SDPUtils.parseCandidate = function(line) {
199	  var parts;
200	  // Parse both variants.
201	  if (line.indexOf('a=candidate:') === 0) {
202	    parts = line.substring(12).split(' ');
203	  } else {
204	    parts = line.substring(10).split(' ');
205	  }
206
207	  var candidate = {
208	    foundation: parts[0],
209	    component: parts[1],
210	    protocol: parts[2].toLowerCase(),
211	    priority: parseInt(parts[3], 10),
212	    ip: parts[4],
213	    port: parseInt(parts[5], 10),
214	    // skip parts[6] == 'typ'
215	    type: parts[7]
216	  };
217
218	  for (var i = 8; i < parts.length; i += 2) {
219	    switch (parts[i]) {
220	      case 'raddr':
221	        candidate.relatedAddress = parts[i + 1];
222	        break;
223	      case 'rport':
224	        candidate.relatedPort = parseInt(parts[i + 1], 10);
225	        break;
226	      case 'tcptype':
227	        candidate.tcpType = parts[i + 1];
228	        break;
229	      default: // Unknown extensions are silently ignored.
230	        break;
231	    }
232	  }
233	  return candidate;
234	};
235
236	// Translates a candidate object into SDP candidate attribute.
237	SDPUtils.writeCandidate = function(candidate) {
238	  var sdp = [];
239	  sdp.push(candidate.foundation);
240	  sdp.push(candidate.component);
241	  sdp.push(candidate.protocol.toUpperCase());
242	  sdp.push(candidate.priority);
243	  sdp.push(candidate.ip);
244	  sdp.push(candidate.port);
245
246	  var type = candidate.type;
247	  sdp.push('typ');
248	  sdp.push(type);
249	  if (type !== 'host' && candidate.relatedAddress &&
250	      candidate.relatedPort) {
251	    sdp.push('raddr');
252	    sdp.push(candidate.relatedAddress); // was: relAddr
253	    sdp.push('rport');
254	    sdp.push(candidate.relatedPort); // was: relPort
255	  }
256	  if (candidate.tcpType && candidate.protocol.toLowerCase() === 'tcp') {
257	    sdp.push('tcptype');
258	    sdp.push(candidate.tcpType);
259	  }
260	  return 'candidate:' + sdp.join(' ');
261	};
262
263	// Parses an rtpmap line, returns RTCRtpCoddecParameters. Sample input:
264	// a=rtpmap:111 opus/48000/2
265	SDPUtils.parseRtpMap = function(line) {
266	  var parts = line.substr(9).split(' ');
267	  var parsed = {
268	    payloadType: parseInt(parts.shift(), 10) // was: id
269	  };
270
271	  parts = parts[0].split('/');
272
273	  parsed.name = parts[0];
274	  parsed.clockRate = parseInt(parts[1], 10); // was: clockrate
275	  // was: channels
276	  parsed.numChannels = parts.length === 3 ? parseInt(parts[2], 10) : 1;
277	  return parsed;
278	};
279
280	// Generate an a=rtpmap line from RTCRtpCodecCapability or
281	// RTCRtpCodecParameters.
282	SDPUtils.writeRtpMap = function(codec) {
283	  var pt = codec.payloadType;
284	  if (codec.preferredPayloadType !== undefined) {
285	    pt = codec.preferredPayloadType;
286	  }
287	  return 'a=rtpmap:' + pt + ' ' + codec.name + '/' + codec.clockRate +
288	      (codec.numChannels !== 1 ? '/' + codec.numChannels : '') + '\r\n';
289	};
290
291	// Parses an a=extmap line (headerextension from RFC 5285). Sample input:
292	// a=extmap:2 urn:ietf:params:rtp-hdrext:toffset
293	SDPUtils.parseExtmap = function(line) {
294	  var parts = line.substr(9).split(' ');
295	  return {
296	    id: parseInt(parts[0], 10),
297	    uri: parts[1]
298	  };
299	};
300
301	// Generates a=extmap line from RTCRtpHeaderExtensionParameters or
302	// RTCRtpHeaderExtension.
303	SDPUtils.writeExtmap = function(headerExtension) {
304	  return 'a=extmap:' + (headerExtension.id || headerExtension.preferredId) +
305	       ' ' + headerExtension.uri + '\r\n';
306	};
307
308	// Parses an ftmp line, returns dictionary. Sample input:
309	// a=fmtp:96 vbr=on;cng=on
310	// Also deals with vbr=on; cng=on
311	SDPUtils.parseFmtp = function(line) {
312	  var parsed = {};
313	  var kv;
314	  var parts = line.substr(line.indexOf(' ') + 1).split(';');
315	  for (var j = 0; j < parts.length; j++) {
316	    kv = parts[j].trim().split('=');
317	    parsed[kv[0].trim()] = kv[1];
318	  }
319	  return parsed;
320	};
321
322	// Generates an a=ftmp line from RTCRtpCodecCapability or RTCRtpCodecParameters.
323	SDPUtils.writeFmtp = function(codec) {
324	  var line = '';
325	  var pt = codec.payloadType;
326	  if (codec.preferredPayloadType !== undefined) {
327	    pt = codec.preferredPayloadType;
328	  }
329	  if (codec.parameters && Object.keys(codec.parameters).length) {
330	    var params = [];
331	    Object.keys(codec.parameters).forEach(function(param) {
332	      params.push(param + '=' + codec.parameters[param]);
333	    });
334	    line += 'a=fmtp:' + pt + ' ' + params.join(';') + '\r\n';
335	  }
336	  return line;
337	};
338
339	// Parses an rtcp-fb line, returns RTCPRtcpFeedback object. Sample input:
340	// a=rtcp-fb:98 nack rpsi
341	SDPUtils.parseRtcpFb = function(line) {
342	  var parts = line.substr(line.indexOf(' ') + 1).split(' ');
343	  return {
344	    type: parts.shift(),
345	    parameter: parts.join(' ')
346	  };
347	};
348	// Generate a=rtcp-fb lines from RTCRtpCodecCapability or RTCRtpCodecParameters.
349	SDPUtils.writeRtcpFb = function(codec) {
350	  var lines = '';
351	  var pt = codec.payloadType;
352	  if (codec.preferredPayloadType !== undefined) {
353	    pt = codec.preferredPayloadType;
354	  }
355	  if (codec.rtcpFeedback && codec.rtcpFeedback.length) {
356	    // FIXME: special handling for trr-int?
357	    codec.rtcpFeedback.forEach(function(fb) {
358	      lines += 'a=rtcp-fb:' + pt + ' ' + fb.type +
359	      (fb.parameter && fb.parameter.length ? ' ' + fb.parameter : '') +
360	          '\r\n';
361	    });
362	  }
363	  return lines;
364	};
365
366	// Parses an RFC 5576 ssrc media attribute. Sample input:
367	// a=ssrc:3735928559 cname:something
368	SDPUtils.parseSsrcMedia = function(line) {
369	  var sp = line.indexOf(' ');
370	  var parts = {
371	    ssrc: parseInt(line.substr(7, sp - 7), 10)
372	  };
373	  var colon = line.indexOf(':', sp);
374	  if (colon > -1) {
375	    parts.attribute = line.substr(sp + 1, colon - sp - 1);
376	    parts.value = line.substr(colon + 1);
377	  } else {
378	    parts.attribute = line.substr(sp + 1);
379	  }
380	  return parts;
381	};
382
383	// Extracts DTLS parameters from SDP media section or sessionpart.
384	// FIXME: for consistency with other functions this should only
385	//   get the fingerprint line as input. See also getIceParameters.
386	SDPUtils.getDtlsParameters = function(mediaSection, sessionpart) {
387	  var lines = SDPUtils.splitLines(mediaSection);
388	  // Search in session part, too.
389	  lines = lines.concat(SDPUtils.splitLines(sessionpart));
390	  var fpLine = lines.filter(function(line) {
391	    return line.indexOf('a=fingerprint:') === 0;
392	  })[0].substr(14);
393	  // Note: a=setup line is ignored since we use the 'auto' role.
394	  var dtlsParameters = {
395	    role: 'auto',
396	    fingerprints: [{
397	      algorithm: fpLine.split(' ')[0],
398	      value: fpLine.split(' ')[1]
399	    }]
400	  };
401	  return dtlsParameters;
402	};
403
404	// Serializes DTLS parameters to SDP.
405	SDPUtils.writeDtlsParameters = function(params, setupType) {
406	  var sdp = 'a=setup:' + setupType + '\r\n';
407	  params.fingerprints.forEach(function(fp) {
408	    sdp += 'a=fingerprint:' + fp.algorithm + ' ' + fp.value + '\r\n';
409	  });
410	  return sdp;
411	};
412	// Parses ICE information from SDP media section or sessionpart.
413	// FIXME: for consistency with other functions this should only
414	//   get the ice-ufrag and ice-pwd lines as input.
415	SDPUtils.getIceParameters = function(mediaSection, sessionpart) {
416	  var lines = SDPUtils.splitLines(mediaSection);
417	  // Search in session part, too.
418	  lines = lines.concat(SDPUtils.splitLines(sessionpart));
419	  var iceParameters = {
420	    usernameFragment: lines.filter(function(line) {
421	      return line.indexOf('a=ice-ufrag:') === 0;
422	    })[0].substr(12),
423	    password: lines.filter(function(line) {
424	      return line.indexOf('a=ice-pwd:') === 0;
425	    })[0].substr(10)
426	  };
427	  return iceParameters;
428	};
429
430	// Serializes ICE parameters to SDP.
431	SDPUtils.writeIceParameters = function(params) {
432	  return 'a=ice-ufrag:' + params.usernameFragment + '\r\n' +
433	      'a=ice-pwd:' + params.password + '\r\n';
434	};
435
436	// Parses the SDP media section and returns RTCRtpParameters.
437	SDPUtils.parseRtpParameters = function(mediaSection) {
438	  var description = {
439	    codecs: [],
440	    headerExtensions: [],
441	    fecMechanisms: [],
442	    rtcp: []
443	  };
444	  var lines = SDPUtils.splitLines(mediaSection);
445	  var mline = lines[0].split(' ');
446	  for (var i = 3; i < mline.length; i++) { // find all codecs from mline[3..]
447	    var pt = mline[i];
448	    var rtpmapline = SDPUtils.matchPrefix(
vendor: 4,376 bytes, lines 449-573
449	        mediaSection, 'a=rtpmap:' + pt + ' ')[0];
450	    if (rtpmapline) {
451	      var codec = SDPUtils.parseRtpMap(rtpmapline);
452	      var fmtps = SDPUtils.matchPrefix(
453	          mediaSection, 'a=fmtp:' + pt + ' ');
454	      // Only the first a=fmtp:<pt> is considered.
455	      codec.parameters = fmtps.length ? SDPUtils.parseFmtp(fmtps[0]) : {};
456	      codec.rtcpFeedback = SDPUtils.matchPrefix(
457	          mediaSection, 'a=rtcp-fb:' + pt + ' ')
458	        .map(SDPUtils.parseRtcpFb);
459	      description.codecs.push(codec);
460	      // parse FEC mechanisms from rtpmap lines.
461	      switch (codec.name.toUpperCase()) {
462	        case 'RED':
463	        case 'ULPFEC':
464	          description.fecMechanisms.push(codec.name.toUpperCase());
465	          break;
466	        default: // only RED and ULPFEC are recognized as FEC mechanisms.
467	          break;
468	      }
469	    }
470	  }
471	  SDPUtils.matchPrefix(mediaSection, 'a=extmap:').forEach(function(line) {
472	    description.headerExtensions.push(SDPUtils.parseExtmap(line));
473	  });
474	  // FIXME: parse rtcp.
475	  return description;
476	};
477
478	// Generates parts of the SDP media section describing the capabilities /
479	// parameters.
480	SDPUtils.writeRtpDescription = function(kind, caps) {
481	  var sdp = '';
482
483	  // Build the mline.
484	  sdp += 'm=' + kind + ' ';
485	  sdp += caps.codecs.length > 0 ? '9' : '0'; // reject if no codecs.
486	  sdp += ' UDP/TLS/RTP/SAVPF ';
487	  sdp += caps.codecs.map(function(codec) {
488	    if (codec.preferredPayloadType !== undefined) {
489	      return codec.preferredPayloadType;
490	    }
491	    return codec.payloadType;
492	  }).join(' ') + '\r\n';
493
494	  sdp += 'c=IN IP4 0.0.0.0\r\n';
495	  sdp += 'a=rtcp:9 IN IP4 0.0.0.0\r\n';
496
497	  // Add a=rtpmap lines for each codec. Also fmtp and rtcp-fb.
498	  caps.codecs.forEach(function(codec) {
499	    sdp += SDPUtils.writeRtpMap(codec);
500	    sdp += SDPUtils.writeFmtp(codec);
501	    sdp += SDPUtils.writeRtcpFb(codec);
502	  });
503	  // FIXME: add headerExtensions, fecMechanismş and rtcp.
504	  sdp += 'a=rtcp-mux\r\n';
505	  return sdp;
506	};
507
508	// Parses the SDP media section and returns an array of
509	// RTCRtpEncodingParameters.
510	SDPUtils.parseRtpEncodingParameters = function(mediaSection) {
511	  var encodingParameters = [];
512	  var description = SDPUtils.parseRtpParameters(mediaSection);
513	  var hasRed = description.fecMechanisms.indexOf('RED') !== -1;
514	  var hasUlpfec = description.fecMechanisms.indexOf('ULPFEC') !== -1;
515
516	  // filter a=ssrc:... cname:, ignore PlanB-msid
517	  var ssrcs = SDPUtils.matchPrefix(mediaSection, 'a=ssrc:')
518	  .map(function(line) {
519	    return SDPUtils.parseSsrcMedia(line);
520	  })
521	  .filter(function(parts) {
522	    return parts.attribute === 'cname';
523	  });
524	  var primarySsrc = ssrcs.length > 0 && ssrcs[0].ssrc;
525	  var secondarySsrc;
526
527	  var flows = SDPUtils.matchPrefix(mediaSection, 'a=ssrc-group:FID')
528	  .map(function(line) {
529	    var parts = line.split(' ');
530	    parts.shift();
531	    return parts.map(function(part) {
532	      return parseInt(part, 10);
533	    });
534	  });
535	  if (flows.length > 0 && flows[0].length > 1 && flows[0][0] === primarySsrc) {
536	    secondarySsrc = flows[0][1];
537	  }
538
539	  description.codecs.forEach(function(codec) {
540	    if (codec.name.toUpperCase() === 'RTX' && codec.parameters.apt) {
541	      var encParam = {
542	        ssrc: primarySsrc,
543	        codecPayloadType: parseInt(codec.parameters.apt, 10),
544	        rtx: {
545	          payloadType: codec.payloadType,
546	          ssrc: secondarySsrc
547	        }
548	      };
549	      encodingParameters.push(encParam);
550	      if (hasRed) {
551	        encParam = JSON.parse(JSON.stringify(encParam));
552	        encParam.fec = {
553	          ssrc: secondarySsrc,
554	          mechanism: hasUlpfec ? 'red+ulpfec' : 'red'
555	        };
556	        encodingParameters.push(encParam);
557	      }
558	    }
559	  });
560	  if (encodingParameters.length === 0 && primarySsrc) {
561	    encodingParameters.push({
562	      ssrc: primarySsrc
563	    });
564	  }
565
566	  // we support both b=AS and b=TIAS but interpret AS as TIAS.
567	  var bandwidth = SDPUtils.matchPrefix(mediaSection, 'b=');
568	  if (bandwidth.length) {
569	    if (bandwidth[0].indexOf('b=TIAS:') === 0) {
570	      bandwidth = parseInt(bandwidth[0].substr(7), 10);
571	    } else if (bandwidth[0].indexOf('b=AS:') === 0) {
572	      bandwidth = parseInt(bandwidth[0].substr(5), 10);
573	    }
574	    encodingParameters.forEach(function(params) {
575	      params.maxBitrate = bandwidth;
576	    });
577	  }
578	  return encodingParameters;
579	};
580
581	SDPUtils.writeSessionBoilerplate = function() {
582	  // FIXME: sess-id should be an NTP timestamp.
583	  return 'v=0\r\n' +
584	      'o=thisisadapterortc 8169639915646943137 2 IN IP4 127.0.0.1\r\n' +
585	      's=-\r\n' +
586	      't=0 0\r\n';
587	};
588
589	SDPUtils.writeMediaSection = function(transceiver, caps, type, stream) {
590	  var sdp = SDPUtils.writeRtpDescription(transceiver.kind, caps);
591
592	  // Map ICE parameters (ufrag, pwd) to SDP.
593	  sdp += SDPUtils.writeIceParameters(
594	      transceiver.iceGatherer.getLocalParameters());
595
596	  // Map DTLS parameters to SDP.
597	  sdp += SDPUtils.writeDtlsParameters(
598	      transceiver.dtlsTransport.getLocalParameters(),
599	      type === 'offer' ? 'actpass' : 'active');
600
601	  sdp += 'a=mid:' + transceiver.mid + '\r\n';
602
603	  if (transceiver.rtpSender && transceiver.rtpReceiver) {
604	    sdp += 'a=sendrecv\r\n';
605	  } else if (transceiver.rtpSender) {
606	    sdp += 'a=sendonly\r\n';
607	  } else if (transceiver.rtpReceiver) {
608	    sdp += 'a=recvonly\r\n';
609	  } else {
610	    sdp += 'a=inactive\r\n';
611	  }
612
613	  // FIXME: for RTX there might be multiple SSRCs. Not implemented in Edge yet.
614	  if (transceiver.rtpSender) {
615	    var msid = 'msid:' + stream.id + ' ' +
616	        transceiver.rtpSender.track.id + '\r\n';
617	    sdp += 'a=' + msid;
618	    sdp += 'a=ssrc:' + transceiver.sendEncodingParameters[0].ssrc +
619	        ' ' + msid;
620	  }
621	  // FIXME: this should be written by writeRtpDescription.
622	  sdp += 'a=ssrc:' + transceiver.sendEncodingParameters[0].ssrc +
623	      ' cname:' + SDPUtils.localCName + '\r\n';
624	  return sdp;
625	};
626
627	// Gets the direction from the mediaSection or the sessionpart.
628	SDPUtils.getDirection = function(mediaSection, sessionpart) {
629	  // Look for sendrecv, sendonly, recvonly, inactive, default to sendrecv.
630	  var lines = SDPUtils.splitLines(mediaSection);
631	  for (var i = 0; i < lines.length; i++) {
632	    switch (lines[i]) {
633	      case 'a=sendrecv':
634	      case 'a=sendonly':
635	      case 'a=recvonly':
636	      case 'a=inactive':
637	        return lines[i].substr(2);
638	      default:
639	        // FIXME: What should happen here?
640	    }
641	  }
642	  if (sessionpart) {
643	    return SDPUtils.getDirection(sessionpart);
644	  }
645	  return 'sendrecv';
646	};
647
648	// Expose public methods.
649	module.exports = SDPUtils;
650
651	},{}],2:[function(require,module,exports){
652	/*
653	 *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
654	 *
655	 *  Use of this source code is governed by a BSD-style license
656	 *  that can be found in the LICENSE file in the root of the source
657	 *  tree.
658	 */
659	 /* eslint-env node */
660
661	'use strict';
662
663	// Shimming starts here.
664	(function() {
665	  // Utils.
666	  var logging = require('./utils').log;
667	  var browserDetails = require('./utils').browserDetails;
668	  // Export to the adapter global object visible in the browser.
669	  module.exports.browserDetails = browserDetails;
670	  module.exports.extractVersion = require('./utils').extractVersion;
671	  module.exports.disableLog = require('./utils').disableLog;
672
673	  // Uncomment the line below if you want logging to occur, including logging
674	  // for the switch statement below. Can also be turned on in the browser via
675	  // adapter.disableLog(false), but then logging from the switch statement below
676	  // will not appear.
677	  // require('./utils').disableLog(false);
678
679	  // Browser shims.
680	  var chromeShim = require('./chrome/chrome_shim') || null;
681	  var edgeShim = require('./edge/edge_shim') || null;
682	  var firefoxShim = require('./firefox/firefox_shim') || null;
683	  var safariShim = require('./safari/safari_shim') || null;
684
685	  // Shim browser if found.
686	  switch (browserDetails.browser) {
687	    case 'opera': // fallthrough as it uses chrome shims
688	    case 'chrome':
689	      if (!chromeShim || !chromeShim.shimPeerConnection) {
690	        logging('Chrome shim is not included in this adapter release.');
691	        return;
692	      }
693	      logging('adapter.js shimming chrome.');
694	      // Export to the adapter global object visible in the browser.
695	      module.exports.browserShim = chromeShim;
696
697	      chromeShim.shimGetUserMedia();
698	      chromeShim.shimMediaStream();
699	      chromeShim.shimSourceObject();
700	      chromeShim.shimPeerConnection();
701	      chromeShim.shimOnTrack();
702	      break;
703	    case 'firefox':
704	      if (!firefoxShim || !firefoxShim.shimPeerConnection) {
705	        logging('Firefox shim is not included in this adapter release.');
706	        return;
707	      }
708	      logging('adapter.js shimming firefox.');
709	      // Export to the adapter global object visible in the browser.
710	      module.exports.browserShim = firefoxShim;
711
712	      firefoxShim.shimGetUserMedia();
713	      firefoxShim.shimSourceObject();
714	      firefoxShim.shimPeerConnection();
715	      firefoxShim.shimOnTrack();
716	      break;
717	    case 'edge':
718	      if (!edgeShim || !edgeShim.shimPeerConnection) {
719	        logging('MS edge shim is not included in this adapter release.');
720	        return;
721	      }
722	      logging('adapter.js shimming edge.');
723	      // Export to the adapter global object visible in the browser.
724	      module.exports.browserShim = edgeShim;
725
726	      edgeShim.shimGetUserMedia();
727	      edgeShim.shimPeerConnection();
728	      break;
729	    case 'safari':
730	      if (!safariShim) {
731	        logging('Safari shim is not included in this adapter release.');
732	        return;
733	      }
734	      logging('adapter.js shimming safari.');
735	      // Export to the adapter global object visible in the browser.
736	      module.exports.browserShim = safariShim;
737
738	      safariShim.shimGetUserMedia();
739	      break;
740	    default:
741	      logging('Unsupported browser!');
742	  }
743	})();
744
745	},{"./chrome/chrome_shim":3,"./edge/edge_shim":5,"./firefox/firefox_shim":7,"./safari/safari_shim":9,"./utils":10}],3:[function(require,module,exports){
746
747	/*
748	 *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
749	 *
750	 *  Use of this source code is governed by a BSD-style license
751	 *  that can be found in the LICENSE file in the root of the source
752	 *  tree.
753	 */
754	 /* eslint-env node */
755	'use strict';
756	var logging = require('../utils.js').log;
757	var browserDetails = require('../utils.js').browserDetails;
758
759	var chromeShim = {
760	  shimMediaStream: function() {
761	    window.MediaStream = window.MediaStream || window.webkitMediaStream;
762	  },
763
764	  shimOnTrack: function() {
765	    if (typeof window === 'object' && window.RTCPeerConnection && !('ontrack' in
766	        window.RTCPeerConnection.prototype)) {
767	      Object.defineProperty(window.RTCPeerConnection.prototype, 'ontrack', {
768	        get: function() {
769	          return this._ontrack;
770	        },
771	        set: function(f) {
772	          var self = this;
773	          if (this._ontrack) {
774	            this.removeEventListener('track', this._ontrack);
775	            this.removeEventListener('addstream', this._ontrackpoly);
776	          }
777	          this.addEventListener('track', this._ontrack = f);
778	          this.addEventListener('addstream', this._ontrackpoly = function(e) {
779	            // onaddstream does not fire when a track is added to an existing
780	            // stream. But stream.onaddtrack is implemented so we use that.
781	            e.stream.addEventListener('addtrack', function(te) {
782	              var event = new Event('track');
783	              event.track = te.track;
784	              event.receiver = {track: te.track};
785	              event.streams = [e.stream];
786	              self.dispatchEvent(event);
787	            });
788	            e.stream.getTracks().forEach(function(track) {
789	              var event = new Event('track');
790	              event.track = track;
791	              event.receiver = {track: track};
792	              event.streams = [e.stream];
793	              this.dispatchEvent(event);
794	            }.bind(this));
795	          }.bind(this));
796	        }
797	      });
798	    }
799	  },
800
801	  shimSourceObject: function() {
802	    if (typeof window === 'object') {
803	      if (window.HTMLMediaElement &&
804	        !('srcObject' in window.HTMLMediaElement.prototype)) {
805	        // Shim the srcObject property, once, when HTMLMediaElement is found.
806	        Object.defineProperty(window.HTMLMediaElement.prototype, 'srcObject', {
807	          get: function() {
808	            return this._srcObject;
809	          },
810	          set: function(stream) {
811	            var self = this;
812	            // Use _srcObject as a private property for this shim
813	            this._srcObject = stream;
814	            if (this.src) {
815	              URL.revokeObjectURL(this.src);
816	            }
817
818	            if (!stream) {
819	              this.src = '';
820	              return;
821	            }
822	            this.src = URL.createObjectURL(stream);
823	            // We need to recreate the blob url when a track is added or
824	            // removed. Doing it manually since we want to avoid a recursion.
825	            stream.addEventListener('addtrack', function() {
826	              if (self.src) {
827	                URL.revokeObjectURL(self.src);
828	              }
829	              self.src = URL.createObjectURL(stream);
830	            });
831	            stream.addEventListener('removetrack', function() {
832	              if (self.src) {
833	                URL.revokeObjectURL(self.src);
834	              }
835	              self.src = URL.createObjectURL(stream);
836	            });
837	          }
838	        });
839	      }
840	    }
841	  },
842
843	  shimPeerConnection: function() {
844	    // The RTCPeerConnection object.
845	    window.RTCPeerConnection = function(pcConfig, pcConstraints) {
846	      // Translate iceTransportPolicy to iceTransports,
847	      // see https://code.google.com/p/webrtc/issues/detail?id=4869
848	      logging('PeerConnection');
849	      if (pcConfig && pcConfig.iceTransportPolicy) {
850	        pcConfig.iceTransports = pcConfig.iceTransportPolicy;
851	      }
852
853	      var pc = new webkitRTCPeerConnection(pcConfig, pcConstraints);
854	      var origGetStats = pc.getStats.bind(pc);
855	      pc.getStats = function(selector, successCallback, errorCallback) {
856	        var self = this;
857	        var args = arguments;
858
859	        // If selector is a function then we are in the old style stats so just
860	        // pass back the original getStats format to avoid breaking old users.
861	        if (arguments.length > 0 && typeof selector === 'function') {
862	          return origGetStats(selector, successCallback);
863	        }
864
865	        var fixChromeStats_ = function(response) {
866	          var standardReport = {};
867	          var reports = response.result();
868	          reports.forEach(function(report) {
869	            var standardStats = {
870	              id: report.id,
871	              timestamp: report.timestamp,
872	              type: report.type
873	            };
874	            report.names().forEach(function(name) {
875	              standardStats[name] = report.stat(name);
876	            });
877	            standardReport[standardStats.id] = standardStats;
878	          });
879
880	          return standardReport;
881	        };
882
883	        // shim getStats with maplike support
884	        var makeMapStats = function(stats, legacyStats) {
885	          var map = new Map(Object.keys(stats).map(function(key) {
886	            return[key, stats[key]];
887	          }));
888	          legacyStats = legacyStats || stats;
889	          Object.keys(legacyStats).forEach(function(key) {
890	            map[key] = legacyStats[key];
891	          });
892	          return map;
893	        };
894
895	        if (arguments.length >= 2) {
896	          var successCallbackWrapper_ = function(response) {
897	            args[1](makeMapStats(fixChromeStats_(response)));
898	          };
899
900	          return origGetStats.apply(this, [successCallbackWrapper_,
901	              arguments[0]]);
902	        }
903
904	        // promise-support
905	        return new Promise(function(resolve, reject) {
906	          if (args.length === 1 && typeof selector === 'object') {
907	            origGetStats.apply(self, [
908	              function(response) {
909	                resolve(makeMapStats(fixChromeStats_(response)));
910	              }, reject]);
911	          } else {
912	            // Preserve legacy chrome stats only on legacy access of stats obj
913	            origGetStats.apply(self, [
914	              function(response) {
915	                resolve(makeMapStats(fixChromeStats_(response),
916	                    response.result()));
917	              }, reject]);
918	          }
919	        }).then(successCallback, errorCallback);
920	      };
921
922	      return pc;
923	    };
924	    window.RTCPeerConnection.prototype = webkitRTCPeerConnection.prototype;
925
926	    // wrap static methods. Currently just generateCertificate.
927	    if (webkitRTCPeerConnection.generateCertificate) {
928	      Object.defineProperty(window.RTCPeerConnection, 'generateCertificate', {
929	        get: function() {
930	          return webkitRTCPeerConnection.generateCertificate;
931	        }
932	      });
933	    }
934
935	    ['createOffer', 'createAnswer'].forEach(function(method) {
936	      var nativeMethod = webkitRTCPeerConnection.prototype[method];
937	      webkitRTCPeerConnection.prototype[method] = function() {
938	        var self = this;
939	        if (arguments.length < 1 || (arguments.length === 1 &&
940	            typeof arguments[0] === 'object')) {
941	          var opts = arguments.length === 1 ? arguments[0] : undefined;
942	          return new Promise(function(resolve, reject) {
943	            nativeMethod.apply(self, [resolve, reject, opts]);
944	          });
945	        }
946	        return nativeMethod.apply(this, arguments);
947	      };
948	    });
949
950	    // add promise support -- natively available in Chrome 51
951	    if (browserDetails.version < 51) {
952	      ['setLocalDescription', 'setRemoteDescription', 'addIceCandidate']
953	          .forEach(function(method) {
954	            var nativeMethod = webkitRTCPeerConnection.prototype[method];
955	            webkitRTCPeerConnection.prototype[method] = function() {
956	              var args = arguments;
957	              var self = this;
958	              var promise = new Promise(function(resolve, reject) {
959	                nativeMethod.apply(self, [args[0], resolve, reject]);
960	              });
961	              if (args.length < 2) {
962	                return promise;
963	              }
964	              return promise.then(function() {
965	                args[1].apply(null, []);
966	              },
967	              function(err) {
968	                if (args.length >= 3) {
969	                  args[2].apply(null, [err]);
970	                }
971	              });
972	            };
973	          });
974	    }
975
976	    // shim implicit creation of RTCSessionDescription/RTCIceCandidate
977	    ['setLocalDescription', 'setRemoteDescription', 'addIceCandidate']
978	        .forEach(function(method) {
979	          var nativeMethod = webkitRTCPeerConnection.prototype[method];
980	          webkitRTCPeerConnection.prototype[method] = function() {
981	            arguments[0] = new ((method === 'addIceCandidate') ?
982	                RTCIceCandidate : RTCSessionDescription)(arguments[0]);
983	            return nativeMethod.apply(this, arguments);
984	          };
985	        });
986
987	    // support for addIceCandidate(null or undefined)
988	    var nativeAddIceCandidate =
989	        RTCPeerConnection.prototype.addIceCandidate;
990	    RTCPeerConnection.prototype.addIceCandidate = function() {
991	      if (!arguments[0]) {
992	        if (arguments[1]) {
993	          arguments[1].apply(null);
994	        }
995	        return Promise.resolve();
996	      }
997	      return nativeAddIceCandidate.apply(this, arguments);
998	    };
999	  }
1000	};
1001
1002
1003	// Expose public methods.
1004	module.exports = {
1005	  shimMediaStream: chromeShim.shimMediaStream,
1006	  shimOnTrack: chromeShim.shimOnTrack,
1007	  shimSourceObject: chromeShim.shimSourceObject,
1008	  shimPeerConnection: chromeShim.shimPeerConnection,
1009	  shimGetUserMedia: require('./getusermedia')
1010	};
1011
1012	},{"../utils.js":10,"./getusermedia":4}],4:[function(require,module,exports){
1013	/*
1014	 *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
1015	 *
1016	 *  Use of this source code is governed by a BSD-style license
1017	 *  that can be found in the LICENSE file in the root of the source
1018	 *  tree.
1019	 */
1020	 /* eslint-env node */
1021	'use strict';
1022	var logging = require('../utils.js').log;
1023
1024	// Expose public methods.
1025	module.exports = function() {
1026	  var constraintsToChrome_ = function(c) {
1027	    if (typeof c !== 'object' || c.mandatory || c.optional) {
1028	      return c;
1029	    }
1030	    var cc = {};
1031	    Object.keys(c).forEach(function(key) {
1032	      if (key === 'require' || key === 'advanced' || key === 'mediaSource') {
1033	        return;
1034	      }
1035	      var r = (typeof c[key] === 'object') ? c[key] : {ideal: c[key]};
1036	      if (r.exact !== undefined && typeof r.exact === 'number') {
1037	        r.min = r.max = r.exact;
1038	      }
1039	      var oldname_ = function(prefix, name) {
1040	        if (prefix) {
1041	          return prefix + name.charAt(0).toUpperCase() + name.slice(1);
1042	        }
1043	        return (name === 'deviceId') ? 'sourceId' : name;
1044	      };
1045	      if (r.ideal !== undefined) {
1046	        cc.optional = cc.optional || [];
1047	        var oc = {};
1048	        if (typeof r.ideal === 'number') {
1049	          oc[oldname_('min', key)] = r.ideal;
1050	          cc.optional.push(oc);
1051	          oc = {};
1052	          oc[oldname_('max', key)] = r.ideal;
1053	          cc.optional.push(oc);
1054	        } else {
1055	          oc[oldname_('', key)] = r.ideal;
1056	          cc.optional.push(oc);
1057	        }
1058	      }
1059	      if (r.exact !== undefined && typeof r.exact !== 'number') {
1060	        cc.mandatory = cc.mandatory || {};
1061	        cc.mandatory[oldname_('', key)] = r.exact;
1062	      } else {
1063	        ['min', 'max'].forEach(function(mix) {
1064	          if (r[mix] !== undefined) {
1065	            cc.mandatory = cc.mandatory || {};
1066	            cc.mandatory[oldname_(mix, key)] = r[mix];
1067	          }
1068	        });
1069	      }
1070	    });
1071	    if (c.advanced) {
1072	      cc.optional = (cc.optional || []).concat(c.advanced);
1073	    }
1074	    return cc;
1075	  };
1076
1077	  var shimConstraints_ = function(constraints, func) {
1078	    constraints = JSON.parse(JSON.stringify(constraints));
1079	    if (constraints && constraints.audio) {
1080	      constraints.audio = constraintsToChrome_(constraints.audio);
1081	    }
1082	    if (constraints && typeof constraints.video === 'object') {
1083	      // Shim facingMode for mobile, where it defaults to "user".
1084	      var face = constraints.video.facingMode;
1085	      face = face && ((typeof face === 'object') ? face : {ideal: face});
1086
1087	      if ((face && (face.exact === 'user' || face.exact === 'environment' ||
1088	                    face.ideal === 'user' || face.ideal === 'environment')) &&
1089	          !(navigator.mediaDevices.getSupportedConstraints &&
1090	            navigator.mediaDevices.getSupportedConstraints().facingMode)) {
1091	        delete constraints.video.facingMode;
1092	        if (face.exact === 'environment' || face.ideal === 'environment') {
1093	          // Look for "back" in label, or use last cam (typically back cam).
1094	          return navigator.mediaDevices.enumerateDevices()
1095	          .then(function(devices) {
1096	            devices = devices.filter(function(d) {
1097	              return d.kind === 'videoinput';
1098	            });
1099	            var back = devices.find(function(d) {
1100	              return d.label.toLowerCase().indexOf('back') !== -1;
1101	            }) || (devices.length && devices[devices.length - 1]);
1102	            if (back) {
1103	              constraints.video.deviceId = face.exact ? {exact: back.deviceId} :
1104	                                                        {ideal: back.deviceId};
1105	            }
1106	            constraints.video = constraintsToChrome_(constraints.video);
1107	            logging('chrome: ' + JSON.stringify(constraints));
1108	            return func(constraints);
1109	          });
1110	        }
1111	      }
1112	      constraints.video = constraintsToChrome_(constraints.video);
1113	    }
1114	    logging('chrome: ' + JSON.stringify(constraints));
1115	    return func(constraints);
1116	  };
1117
1118	  var shimError_ = function(e) {
1119	    return {
1120	      name: {
1121	        PermissionDeniedError: 'NotAllowedError',
1122	        ConstraintNotSatisfiedError: 'OverconstrainedError'
1123	      }[e.name] || e.name,
1124	      message: e.message,
1125	      constraint: e.constraintName,
1126	      toString: function() {
1127	        return this.name + (this.message && ': ') + this.message;
1128	      }
1129	    };
1130	  };
1131
1132	  var getUserMedia_ = function(constraints, onSuccess, onError) {
1133	    shimConstraints_(constraints, function(c) {
1134	      navigator.webkitGetUserMedia(c, onSuccess, function(e) {
1135	        onError(shimError_(e));
1136	      });
1137	    });
1138	  };
1139
1140	  navigator.getUserMedia = getUserMedia_;
1141
1142	  // Returns the result of getUserMedia as a Promise.
1143	  var getUserMediaPromise_ = function(constraints) {
1144	    return new Promise(function(resolve, reject) {
1145	      navigator.getUserMedia(constraints, resolve, reject);
1146	    });
1147	  };
1148
1149	  if (!navigator.mediaDevices) {
1150	    navigator.mediaDevices = {
1151	      getUserMedia: getUserMediaPromise_,
1152	      enumerateDevices: function() {
1153	        return new Promise(function(resolve) {
1154	          var kinds = {audio: 'audioinput', video: 'videoinput'};
1155	          return MediaStreamTrack.getSources(function(devices) {
1156	            resolve(devices.map(function(device) {
1157	              return {label: device.label,
1158	                      kind: kinds[device.kind],
1159	                      deviceId: device.id,
1160	                      groupId: ''};
1161	            }));
1162	          });
1163	        });
1164	      }
1165	    };
1166	  }
1167
1168	  // A shim for getUserMedia method on the mediaDevices object.
1169	  // TODO(KaptenJansson) remove once implemented in Chrome stable.
1170	  if (!navigator.mediaDevices.getUserMedia) {
1171	    navigator.mediaDevices.getUserMedia = function(constraints) {
1172	      return getUserMediaPromise_(constraints);
1173	    };
1174	  } else {
1175	    // Even though Chrome 45 has navigator.mediaDevices and a getUserMedia
1176	    // function which returns a Promise, it does not accept spec-style
1177	    // constraints.
1178	    var origGetUserMedia = navigator.mediaDevices.getUserMedia.
1179	        bind(navigator.mediaDevices);
1180	    navigator.mediaDevices.getUserMedia = function(cs) {
1181	      return shimConstraints_(cs, function(c) {
1182	        return origGetUserMedia(c).then(function(stream) {
1183	          if (c.audio && !stream.getAudioTracks().length ||
1184	              c.video && !stream.getVideoTracks().length) {
1185	            stream.getTracks().forEach(function(track) {
1186	              track.stop();
1187	            });
1188	            throw new DOMException('', 'NotFoundError');
1189	          }
1190	          return stream;
1191	        }, function(e) {
1192	          return Promise.reject(shimError_(e));
1193	        });
1194	      });
1195	    };
1196	  }
1197
1198	  // Dummy devicechange event methods.
1199	  // TODO(KaptenJansson) remove once implemented in Chrome stable.
1200	  if (typeof navigator.mediaDevices.addEventListener === 'undefined') {
1201	    navigator.mediaDevices.addEventListener = function() {
1202	      logging('Dummy mediaDevices.addEventListener called.');
1203	    };
1204	  }
1205	  if (typeof navigator.mediaDevices.removeEventListener === 'undefined') {
1206	    navigator.mediaDevices.removeEventListener = function() {
1207	      logging('Dummy mediaDevices.removeEventListener called.');
1208	    };
1209	  }
1210	};
1211
1212	},{"../utils.js":10}],5:[function(require,module,exports){
1213	/*
1214	 *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
1215	 *
1216	 *  Use of this source code is governed by a BSD-style license
1217	 *  that can be found in the LICENSE file in the root of the source
1218	 *  tree.
1219	 */
1220	 /* eslint-env node */
1221	'use strict';
1222
1223	var SDPUtils = require('sdp');
1224	var browserDetails = require('../utils').browserDetails;
1225
1226	var edgeShim = {
1227	  shimPeerConnection: function() {
1228	    if (window.RTCIceGatherer) {
1229	      // ORTC defines an RTCIceCandidate object but no constructor.
1230	      // Not implemented in Edge.
1231	      if (!window.RTCIceCandidate) {
1232	        window.RTCIceCandidate = function(args) {
1233	          return args;
1234	        };
1235	      }
1236	      // ORTC does not have a session description object but
1237	      // other browsers (i.e. Chrome) that will support both PC and ORTC
1238	      // in the future might have this defined already.
1239	      if (!window.RTCSessionDescription) {
1240	        window.RTCSessionDescription = function(args) {
1241	          return args;
1242	        };
1243	      }
1244	      // this adds an additional event listener to MediaStrackTrack that signals
1245	      // when a tracks enabled property was changed.
1246	      var origMSTEnabled = Object.getOwnPropertyDescriptor(
1247	          MediaStreamTrack.prototype, 'enabled');
1248	      Object.defineProperty(MediaStreamTrack.prototype, 'enabled', {
1249	        set: function(value) {
1250	          origMSTEnabled.set.call(this, value);
1251	          var ev = new Event('enabled');
1252	          ev.enabled = value;
1253	          this.dispatchEvent(ev);
1254	        }
1255	      });
1256	    }
1257
1258	    window.RTCPeerConnection = function(config) {
1259	      var self = this;
1260
1261	      var _eventTarget = document.createDocumentFragment();
1262	      ['addEventListener', 'removeEventListener', 'dispatchEvent']
1263	          .forEach(function(method) {
1264	            self[method] = _eventTarget[method].bind(_eventTarget);
1265	          });
1266
1267	      this.onicecandidate = null;
1268	      this.onaddstream = null;
1269	      this.ontrack = null;
1270	      this.onremovestream = null;
1271	      this.onsignalingstatechange = null;
1272	      this.oniceconnectionstatechange = null;
1273	      this.onnegotiationneeded = null;
1274	      this.ondatachannel = null;
1275
1276	      this.localStreams = [];
1277	      this.remoteStreams = [];
1278	      this.getLocalStreams = function() {
1279	        return self.localStreams;
1280	      };
1281	      this.getRemoteStreams = function() {
1282	        return self.remoteStreams;
1283	      };
1284
1285	      this.localDescription = new RTCSessionDescription({
1286	        type: '',
1287	        sdp: ''
1288	      });
1289	      this.remoteDescription = new RTCSessionDescription({
1290	        type: '',
1291	        sdp: ''
1292	      });
1293	      this.signalingState = 'stable';
1294	      this.iceConnectionState = 'new';
1295	      this.iceGatheringState = 'new';
1296
1297	      this.iceOptions = {
1298	        gatherPolicy: 'all',
1299	        iceServers: []
1300	      };
1301	      if (config && config.iceTransportPolicy) {
1302	        switch (config.iceTransportPolicy) {
1303	          case 'all':
1304	          case 'relay':
1305	            this.iceOptions.gatherPolicy = config.iceTransportPolicy;
1306	            break;
1307	          case 'none':
1308	            // FIXME: remove once implementation and spec have added this.
1309	            throw new TypeError('iceTransportPolicy "none" not supported');
1310	          default:
1311	            // don't set iceTransportPolicy.
1312	            break;
1313	        }
1314	      }
1315	      this.usingBundle = config && config.bundlePolicy === 'max-bundle';
1316
1317	      if (config && config.iceServers) {
1318	        // Edge does not like
1319	        // 1) stun:
1320	        // 2) turn: that does not have all of turn:host:port?transport=udp
1321	        // 3) turn: with ipv6 addresses
1322	        var iceServers = JSON.parse(JSON.stringify(config.iceServers));
1323	        this.iceOptions.iceServers = iceServers.filter(function(server) {
1324	          if (server && server.urls) {
1325	            var urls = server.urls;
1326	            if (typeof urls === 'string') {
1327	              urls = [urls];
1328	            }
1329	            urls = urls.filter(function(url) {
1330	              return (url.indexOf('turn:') === 0 &&
1331	                  url.indexOf('transport=udp') !== -1 &&
1332	                  url.indexOf('turn:[') === -1) ||
1333	                  (url.indexOf('stun:') === 0 &&
1334	                    browserDetails.version >= 14393);
1335	            })[0];
1336	            return !!urls;
1337	          }
1338	          return false;
1339	        });
1340	      }
1341	      this._config = config;
1342
1343	      // per-track iceGathers, iceTransports, dtlsTransports, rtpSenders, ...
1344	      // everything that is needed to describe a SDP m-line.
1345	      this.transceivers = [];
1346
1347	      // since the iceGatherer is currently created in createOffer but we
1348	      // must not emit candidates until after setLocalDescription we buffer
1349	      // them in this array.
1350	      this._localIceCandidatesBuffer = [];
1351	    };
1352
1353	    window.RTCPeerConnection.prototype._emitBufferedCandidates = function() {
1354	      var self = this;
1355	      var sections = SDPUtils.splitSections(self.localDescription.sdp);
1356	      // FIXME: need to apply ice candidates in a way which is async but
1357	      // in-order
1358	      this._localIceCandidatesBuffer.forEach(function(event) {
1359	        var end = !event.candidate || Object.keys(event.candidate).length === 0;
1360	        if (end) {
1361	          for (var j = 1; j < sections.length; j++) {
1362	            if (sections[j].indexOf('\r\na=end-of-candidates\r\n') === -1) {
1363	              sections[j] += 'a=end-of-candidates\r\n';
1364	            }
1365	          }
1366	        } else if (event.candidate.candidate.indexOf('typ endOfCandidates')
1367	            === -1) {
1368	          sections[event.candidate.sdpMLineIndex + 1] +=
1369	              'a=' + event.candidate.candidate + '\r\n';
1370	        }
1371	        self.localDescription.sdp = sections.join('');
1372	        self.dispatchEvent(event);
1373	        if (self.onicecandidate !== null) {
1374	          self.onicecandidate(event);
1375	        }
1376	        if (!event.candidate && self.iceGatheringState !== 'complete') {
1377	          var complete = self.transceivers.every(function(transceiver) {
1378	            return transceiver.iceGatherer &&
1379	                transceiver.iceGatherer.state === 'completed';
1380	          });
1381	          if (complete) {
1382	            self.iceGatheringState = 'complete';
1383	          }
1384	        }
1385	      });
1386	      this._localIceCandidatesBuffer = [];
1387	    };
1388
1389	    window.RTCPeerConnection.prototype.getConfiguration = function() {
1390	      return this._config;
1391	    };
1392
1393	    window.RTCPeerConnection.prototype.addStream = function(stream) {
1394	      // Clone is necessary for local demos mostly, attaching directly
1395	      // to two different senders does not work (build 10547).
1396	      var clonedStream = stream.clone();
1397	      stream.getTracks().forEach(function(track, idx) {
1398	        var clonedTrack = clonedStream.getTracks()[idx];
1399	        track.addEventListener('enabled', function(event) {
1400	          clonedTrack.enabled = event.enabled;
1401	        });
1402	      });
1403	      this.localStreams.push(clonedStream);
1404	      this._maybeFireNegotiationNeeded();
1405	    };
1406
1407	    window.RTCPeerConnection.prototype.removeStream = function(stream) {
1408	      var idx = this.localStreams.indexOf(stream);
1409	      if (idx > -1) {
1410	        this.localStreams.splice(idx, 1);
1411	        this._maybeFireNegotiationNeeded();
1412	      }
1413	    };
1414
1415	    window.RTCPeerConnection.prototype.getSenders = function() {
1416	      return this.transceivers.filter(function(transceiver) {
1417	        return !!transceiver.rtpSender;
1418	      })
1419	      .map(function(transceiver) {
1420	        return transceiver.rtpSender;
1421	      });
1422	    };
1423
1424	    window.RTCPeerConnection.prototype.getReceivers = function() {
1425	      return this.transceivers.filter(function(transceiver) {
1426	        return !!transceiver.rtpReceiver;
1427	      })
1428	      .map(function(transceiver) {
1429	        return transceiver.rtpReceiver;
1430	      });
1431	    };
1432
1433	    // Determines the intersection of local and remote capabilities.
1434	    window.RTCPeerConnection.prototype._getCommonCapabilities =
1435	        function(localCapabilities, remoteCapabilities) {
1436	          var commonCapabilities = {
1437	            codecs: [],
1438	            headerExtensions: [],
1439	            fecMechanisms: []
1440	          };
1441	          localCapabilities.codecs.forEach(function(lCodec) {
1442	            for (var i = 0; i < remoteCapabilities.codecs.length; i++) {
1443	              var rCodec = remoteCapabilities.codecs[i];
1444	              if (lCodec.name.toLowerCase() === rCodec.name.toLowerCase() &&
1445	                  lCodec.clockRate === rCodec.clockRate) {
1446	                // number of channels is the highest common number of channels
1447	                rCodec.numChannels = Math.min(lCodec.numChannels,
1448	                    rCodec.numChannels);
1449	                // push rCodec so we reply with offerer payload type
1450	                commonCapabilities.codecs.push(rCodec);
1451
1452	                // determine common feedback mechanisms
1453	                rCodec.rtcpFeedback = rCodec.rtcpFeedback.filter(function(fb) {
1454	                  for (var j = 0; j < lCodec.rtcpFeedback.length; j++) {
1455	                    if (lCodec.rtcpFeedback[j].type === fb.type &&
1456	                        lCodec.rtcpFeedback[j].parameter === fb.parameter) {
1457	                      return true;
1458	                    }
1459	                  }
1460	                  return false;
1461	                });
1462	                // FIXME: also need to determine .parameters
1463	                //  see https://github.com/openpeer/ortc/issues/569
1464	                break;
1465	              }
1466	            }
1467	          });
1468
1469	          localCapabilities.headerExtensions
1470	              .forEach(function(lHeaderExtension) {
1471	                for (var i = 0; i < remoteCapabilities.headerExtensions.length;
1472	                     i++) {
1473	                  var rHeaderExtension = remoteCapabilities.headerExtensions[i];
1474	                  if (lHeaderExtension.uri === rHeaderExtension.uri) {
1475	                    commonCapabilities.headerExtensions.push(rHeaderExtension);
1476	                    break;
1477	                  }
1478	                }
1479	              });
1480
1481	          // FIXME: fecMechanisms
1482	          return commonCapabilities;
1483	        };
1484
1485	    // Create ICE gatherer, ICE transport and DTLS transport.
1486	    window.RTCPeerConnection.prototype._createIceAndDtlsTransports =
1487	        function(mid, sdpMLineIndex) {
1488	          var self = this;
1489	          var iceGatherer = new RTCIceGatherer(self.iceOptions);
1490	          var iceTransport = new RTCIceTransport(iceGatherer);
1491	          iceGatherer.onlocalcandidate = function(evt) {
1492	            var event = new Event('icecandidate');
1493	            event.candidate = {sdpMid: mid, sdpMLineIndex: sdpMLineIndex};
1494
1495	            var cand = evt.candidate;
1496	            var end = !cand || Object.keys(cand).length === 0;
1497	            // Edge emits an empty object for RTCIceCandidateComplete‥
1498	            if (end) {
1499	              // polyfill since RTCIceGatherer.state is not implemented in
1500	              // Edge 10547 yet.
1501	              if (iceGatherer.state === undefined) {
1502	                iceGatherer.state = 'completed';
1503	              }
1504
1505	              // Emit a candidate with type endOfCandidates to make the samples
1506	              // work. Edge requires addIceCandidate with this empty candidate
1507	              // to start checking. The real solution is to signal
1508	              // end-of-candidates to the other side when getting the null
1509	              // candidate but some apps (like the samples) don't do that.
1510	              event.candidate.candidate =
1511	                  'candidate:1 1 udp 1 0.0.0.0 9 typ endOfCandidates';
1512	            } else {
1513	              // RTCIceCandidate doesn't have a component, needs to be added
1514	              cand.component = iceTransport.component === 'RTCP' ? 2 : 1;
1515	              event.candidate.candidate = SDPUtils.writeCandidate(cand);
1516	            }
1517
1518	            // update local description.
1519	            var sections = SDPUtils.splitSections(self.localDescription.sdp);
1520	            if (event.candidate.candidate.indexOf('typ endOfCandidates')
1521	                === -1) {
1522	              sections[event.candidate.sdpMLineIndex + 1] +=
1523	                  'a=' + event.candidate.candidate + '\r\n';
1524	            } else {
1525	              sections[event.candidate.sdpMLineIndex + 1] +=
1526	                  'a=end-of-candidates\r\n';
1527	            }
1528	            self.localDescription.sdp = sections.join('');
1529
1530	            var complete = self.transceivers.every(function(transceiver) {
1531	              return transceiver.iceGatherer &&
1532	                  transceiver.iceGatherer.state === 'completed';
1533	            });
1534
1535	            // Emit candidate if localDescription is set.
1536	            // Also emits null candidate when all gatherers are complete.
1537	            switch (self.iceGatheringState) {
1538	              case 'new':
1539	                self._localIceCandidatesBuffer.push(event);
1540	                if (end && complete) {
1541	                  self._localIceCandidatesBuffer.push(
1542	                      new Event('icecandidate'));
1543	                }
1544	                break;
1545	              case 'gathering':
1546	                self._emitBufferedCandidates();
1547	                self.dispatchEvent(event);
1548	                if (self.onicecandidate !== null) {
1549	                  self.onicecandidate(event);
1550	                }
1551	                if (complete) {
1552	                  self.dispatchEvent(new Event('icecandidate'));
1553	                  if (self.onicecandidate !== null) {
1554	                    self.onicecandidate(new Event('icecandidate'));
1555	                  }
1556	                  self.iceGatheringState = 'complete';
1557	                }
1558	                break;
1559	              case 'complete':
1560	                // should not happen... currently!
1561	                break;
1562	              default: // no-op.
1563	                break;
1564	            }
1565	          };
1566	          iceTransport.onicestatechange = function() {
1567	            self._updateConnectionState();
1568	          };
1569
1570	          var dtlsTransport = new RTCDtlsTransport(iceTransport);
1571	          dtlsTransport.ondtlsstatechange = function() {
1572	            self._updateConnectionState();
1573	          };
1574	          dtlsTransport.onerror = function() {
1575	            // onerror does not set state to failed by itself.
1576	            dtlsTransport.state = 'failed';
1577	            self._updateConnectionState();
1578	          };
1579
1580	          return {
1581	            iceGatherer: iceGatherer,
1582	            iceTransport: iceTransport,
1583	            dtlsTransport: dtlsTransport
1584	          };
1585	        };
1586
1587	    // Start the RTP Sender and Receiver for a transceiver.
1588	    window.RTCPeerConnection.prototype._transceive = function(transceiver,
1589	        send, recv) {
1590	      var params = this._getCommonCapabilities(transceiver.localCapabilities,
1591	          transceiver.remoteCapabilities);
1592	      if (send && transceiver.rtpSender) {
1593	        params.encodings = transceiver.sendEncodingParameters;
1594	        params.rtcp = {
1595	          cname: SDPUtils.localCName
1596	        };
1597	        if (transceiver.recvEncodingParameters.length) {
1598	          params.rtcp.ssrc = transceiver.recvEncodingParameters[0].ssrc;
1599	        }
1600	        transceiver.rtpSender.send(params);
1601	      }
1602	      if (recv && transceiver.rtpReceiver) {
1603	        // remove RTX field in Edge 14942
1604	        if (transceiver.kind === 'video'
1605	            && transceiver.recvEncodingParameters) {
1606	          transceiver.recvEncodingParameters.forEach(function(p) {
1607	            delete p.rtx;
1608	          });
1609	        }
1610	        params.encodings = transceiver.recvEncodingParameters;
1611	        params.rtcp = {
1612	          cname: transceiver.cname
1613	        };
1614	        if (transceiver.sendEncodingParameters.length) {
1615	          params.rtcp.ssrc = transceiver.sendEncodingParameters[0].ssrc;
1616	        }
1617	        transceiver.rtpReceiver.receive(params);
1618	      }
1619	    };
1620
1621	    window.RTCPeerConnection.prototype.setLocalDescription =
1622	        function(description) {
1623	          var self = this;
1624	          var sections;
1625	          var sessionpart;
1626	          if (description.type === 'offer') {
1627	            // FIXME: What was the purpose of this empty if statement?
1628	            // if (!this._pendingOffer) {
1629	            // } else {
1630	            if (this._pendingOffer) {
1631	              // VERY limited support for SDP munging. Limited to:
1632	              // * changing the order of codecs
1633	              sections = SDPUtils.splitSections(description.sdp);
1634	              sessionpart = sections.shift();
1635	              sections.forEach(function(mediaSection, sdpMLineIndex) {
1636	                var caps = SDPUtils.parseRtpParameters(mediaSection);
1637	                self._pendingOffer[sdpMLineIndex].localCapabilities = caps;
1638	              });
1639	              this.transceivers = this._pendingOffer;
1640	              delete this._pendingOffer;
1641	            }
1642	          } else if (description.type === 'answer') {
1643	            sections = SDPUtils.splitSections(self.remoteDescription.sdp);
1644	            sessionpart = sections.shift();
1645	            var isIceLite = SDPUtils.matchPrefix(sessionpart,
1646	                'a=ice-lite').length > 0;
1647	            sections.forEach(function(mediaSection, sdpMLineIndex) {
1648	              var transceiver = self.transceivers[sdpMLineIndex];
1649	              var iceGatherer = transceiver.iceGatherer;
1650	              var iceTransport = transceiver.iceTransport;
1651	              var dtlsTransport = transceiver.dtlsTransport;
1652	              var localCapabilities = transceiver.localCapabilities;
1653	              var remoteCapabilities = transceiver.remoteCapabilities;
1654
1655	              var rejected = mediaSection.split('\n', 1)[0]
1656	                  .split(' ', 2)[1] === '0';
1657
1658	              if (!rejected && !transceiver.isDatachannel) {
1659	                var remoteIceParameters = SDPUtils.getIceParameters(
1660	                    mediaSection, sessionpart);
1661	                if (isIceLite) {
1662	                  var cands = SDPUtils.matchPrefix(mediaSection, 'a=candidate:')
1663	                  .map(function(cand) {
1664	                    return SDPUtils.parseCandidate(cand);
1665	                  })
1666	                  .filter(function(cand) {
1667	                    return cand.component === '1';
1668	                  });
1669	                  // ice-lite only includes host candidates in the SDP so we can
1670	                  // use setRemoteCandidates (which implies an
1671	                  // RTCIceCandidateComplete)
1672	                  if (cands.length) {
1673	                    iceTransport.setRemoteCandidates(cands);
1674	                  }
1675	                }
1676	                var remoteDtlsParameters = SDPUtils.getDtlsParameters(
1677	                    mediaSection, sessionpart);
1678	                if (isIceLite) {
1679	                  remoteDtlsParameters.role = 'server';
1680	                }
1681
1682	                if (!self.usingBundle || sdpMLineIndex === 0) {
1683	                  iceTransport.start(iceGatherer, remoteIceParameters,
1684	                      isIceLite ? 'controlling' : 'controlled');
1685	                  dtlsTransport.start(remoteDtlsParameters);
1686	                }
1687
1688	                // Calculate intersection of capabilities.
1689	                var params = self._getCommonCapabilities(localCapabilities,
1690	                    remoteCapabilities);
1691
1692	                // Start the RTCRtpSender. The RTCRtpReceiver for this
1693	                // transceiver has already been started in setRemoteDescription.
1694	                self._transceive(transceiver,
1695	                    params.codecs.length > 0,
1696	                    false);
1697	              }
1698	            });
1699	          }
1700
1701	          this.localDescription = {
1702	            type: description.type,
1703	            sdp: description.sdp
1704	          };
1705	          switch (description.type) {
1706	            case 'offer':
1707	              this._updateSignalingState('have-local-offer');
1708	              break;
1709	            case 'answer':
1710	              this._updateSignalingState('stable');
1711	              break;
1712	            default:
1713	              throw new TypeError('unsupported type "' + description.type +
1714	                  '"');
1715	          }
1716
1717	          // If a success callback was provided, emit ICE candidates after it
1718	          // has been executed. Otherwise, emit callback after the Promise is
1719	          // resolved.
1720	          var hasCallback = arguments.length > 1 &&
1721	            typeof arguments[1] === 'function';
1722	          if (hasCallback) {
1723	            var cb = arguments[1];
1724	            window.setTimeout(function() {
1725	              cb();
1726	              if (self.iceGatheringState === 'new') {
1727	                self.iceGatheringState = 'gathering';
1728	              }
1729	              self._emitBufferedCandidates();
1730	            }, 0);
1731	          }
1732	          var p = Promise.resolve();
1733	          p.then(function() {
1734	            if (!hasCallback) {
1735	              if (self.iceGatheringState === 'new') {
1736	                self.iceGatheringState = 'gathering';
1737	              }
1738	              // Usually candidates will be emitted earlier.
1739	              window.setTimeout(self._emitBufferedCandidates.bind(self), 500);
1740	            }
1741	          });
1742	          return p;
1743	        };
1744
1745	    window.RTCPeerConnection.prototype.setRemoteDescription =
1746	        function(description) {
1747	          var self = this;
1748	          var stream = new MediaStream();
1749	          var receiverList = [];
1750	          var sections = SDPUtils.splitSections(description.sdp);
1751	          var sessionpart = sections.shift();
1752	          var isIceLite = SDPUtils.matchPrefix(sessionpart,
1753	              'a=ice-lite').length > 0;
1754	          this.usingBundle = SDPUtils.matchPrefix(sessionpart,
1755	              'a=group:BUNDLE ').length > 0;
1756	          sections.forEach(function(mediaSection, sdpMLineIndex) {
1757	            var lines = SDPUtils.splitLines(mediaSection);
1758	            var mline = lines[0].substr(2).split(' ');
1759	            var kind = mline[0];
1760	            var rejected = mline[1] === '0';
1761	            var direction = SDPUtils.getDirection(mediaSection, sessionpart);
1762
1763	            var mid = SDPUtils.matchPrefix(mediaSection, 'a=mid:');
1764	            if (mid.length) {
1765	              mid = mid[0].substr(6);
1766	            } else {
1767	              mid = SDPUtils.generateIdentifier();
1768	            }
1769
1770	            // Reject datachannels which are not implemented yet.
1771	            if (kind === 'application' && mline[2] === 'DTLS/SCTP') {
1772	              self.transceivers[sdpMLineIndex] = {
1773	                mid: mid,
1774	                isDatachannel: true
1775	              };
1776	              return;
1777	            }
1778
1779	            var transceiver;
1780	            var iceGatherer;
1781	            var iceTransport;
1782	            var dtlsTransport;
1783	            var rtpSender;
1784	            var rtpReceiver;
1785	            var sendEncodingParameters;
1786	            var recvEncodingParameters;
1787	            var localCapabilities;
1788
1789	            var track;
1790	            // FIXME: ensure the mediaSection has rtcp-mux set.
1791	            var remoteCapabilities = SDPUtils.parseRtpParameters(mediaSection);
1792	            var remoteIceParameters;
1793	            var remoteDtlsParameters;
1794	            if (!rejected) {
1795	              remoteIceParameters = SDPUtils.getIceParameters(mediaSection,
1796	                  sessionpart);
1797	              remoteDtlsParameters = SDPUtils.getDtlsParameters(mediaSection,
1798	                  sessionpart);
1799	              remoteDtlsParameters.role = 'client';
1800	            }
1801	            recvEncodingParameters =
1802	                SDPUtils.parseRtpEncodingParameters(mediaSection);
1803
1804	            var cname;
1805	            // Gets the first SSRC. Note that with RTX there might be multiple
1806	            // SSRCs.
1807	            var remoteSsrc = SDPUtils.matchPrefix(mediaSection, 'a=ssrc:')
1808	                .map(function(line) {
1809	                  return SDPUtils.parseSsrcMedia(line);
1810	                })
1811	                .filter(function(obj) {
1812	                  return obj.attribute === 'cname';
1813	                })[0];
1814	            if (remoteSsrc) {
1815	              cname = remoteSsrc.value;
1816	            }
1817
1818	            var isComplete = SDPUtils.matchPrefix(mediaSection,
1819	                'a=end-of-candidates', sessionpart).length > 0;
1820	            var cands = SDPUtils.matchPrefix(mediaSection, 'a=candidate:')
1821	                .map(function(cand) {
1822	                  return SDPUtils.parseCandidate(cand);
1823	                })
1824	                .filter(function(cand) {
1825	                  return cand.component === '1';
1826	                });
1827	            if (description.type === 'offer' && !rejected) {
1828	              var transports = self.usingBundle && sdpMLineIndex > 0 ? {
1829	                iceGatherer: self.transceivers[0].iceGatherer,
1830	                iceTransport: self.transceivers[0].iceTransport,
1831	                dtlsTransport: self.transceivers[0].dtlsTransport
1832	              } : self._createIceAndDtlsTransports(mid, sdpMLineIndex);
1833
1834	              if (isComplete) {
1835	                transports.iceTransport.setRemoteCandidates(cands);
1836	              }
1837
1838	              localCapabilities = RTCRtpReceiver.getCapabilities(kind);
1839
1840	              // filter RTX until additional stuff needed for RTX is implemented
1841	              // in adapter.js
1842	              localCapabilities.codecs = localCapabilities.codecs.filter(
1843	                  function(codec) {
1844	                    return codec.name !== 'rtx';
1845	                  });
1846
1847	              sendEncodingParameters = [{
1848	                ssrc: (2 * sdpMLineIndex + 2) * 1001
1849	              }];
1850
1851	              rtpReceiver = new RTCRtpReceiver(transports.dtlsTransport, kind);
1852
1853	              track = rtpReceiver.track;
1854	              receiverList.push([track, rtpReceiver]);
1855	              // FIXME: not correct when there are multiple streams but that is
1856	              // not currently supported in this shim.
1857	              stream.addTrack(track);
1858
1859	              // FIXME: look at direction.
1860	              if (self.localStreams.length > 0 &&
1861	                  self.localStreams[0].getTracks().length >= sdpMLineIndex) {
1862	                var localTrack;
1863	                if (kind === 'audio') {
1864	                  localTrack = self.localStreams[0].getAudioTracks()[0];
1865	                } else if (kind === 'video') {
1866	                  localTrack = self.localStreams[0].getVideoTracks()[0];
1867	                }
1868	                if (localTrack) {
1869	                  rtpSender = new RTCRtpSender(localTrack,
1870	                      transports.dtlsTransport);
1871	                }
1872	              }
1873
1874	              self.transceivers[sdpMLineIndex] = {
1875	                iceGatherer: transports.iceGatherer,
1876	                iceTransport: transports.iceTransport,
1877	                dtlsTransport: transports.dtlsTransport,
1878	                localCapabilities: localCapabilities,
1879	                remoteCapabilities: remoteCapabilities,
1880	                rtpSender: rtpSender,
1881	                rtpReceiver: rtpReceiver,
1882	                kind: kind,
1883	                mid: mid,
1884	                cname: cname,
1885	                sendEncodingParameters: sendEncodingParameters,
1886	                recvEncodingParameters: recvEncodingParameters
1887	              };
1888	              // Start the RTCRtpReceiver now. The RTPSender is started in
1889	              // setLocalDescription.
1890	              self._transceive(self.transceivers[sdpMLineIndex],
1891	                  false,
1892	                  direction === 'sendrecv' || direction === 'sendonly');
1893	            } else if (description.type === 'answer' && !rejected) {
1894	              transceiver = self.transceivers[sdpMLineIndex];
1895	              iceGatherer = transceiver.iceGatherer;
1896	              iceTransport = transceiver.iceTransport;
1897	              dtlsTransport = transceiver.dtlsTransport;
1898	              rtpSender = transceiver.rtpSender;
1899	              rtpReceiver = transceiver.rtpReceiver;
1900	              sendEncodingParameters = transceiver.sendEncodingParameters;
1901	              localCapabilities = transceiver.localCapabilities;
1902
1903	              self.transceivers[sdpMLineIndex].recvEncodingParameters =
1904	                  recvEncodingParameters;
1905	              self.transceivers[sdpMLineIndex].remoteCapabilities =
1906	                  remoteCapabilities;
1907	              self.transceivers[sdpMLineIndex].cname = cname;
1908
1909	              if ((isIceLite || isComplete) && cands.length) {
1910	                iceTransport.setRemoteCandidates(cands);
1911	              }
1912	              if (!self.usingBundle || sdpMLineIndex === 0) {
1913	                iceTransport.start(iceGatherer, remoteIceParameters,
1914	                    'controlling');
1915	                dtlsTransport.start(remoteDtlsParameters);
1916	              }
1917
1918	              self._transceive(transceiver,
1919	                  direction === 'sendrecv' || direction === 'recvonly',
1920	                  direction === 'sendrecv' || direction === 'sendonly');
1921
1922	              if (rtpReceiver &&
1923	                  (direction === 'sendrecv' || direction === 'sendonly')) {
1924	                track = rtpReceiver.track;
1925	                receiverList.push([track, rtpReceiver]);
1926	                stream.addTrack(track);
1927	              } else {
1928	                // FIXME: actually the receiver should be created later.
1929	                delete transceiver.rtpReceiver;
1930	              }
1931	            }
1932	          });
1933
1934	          this.remoteDescription = {
1935	            type: description.type,
1936	            sdp: description.sdp
1937	          };
1938	          switch (description.type) {
1939	            case 'offer':
1940	              this._updateSignalingState('have-remote-offer');
1941	              break;
1942	            case 'answer':
1943	              this._updateSignalingState('stable');
1944	              break;
1945	            default:
1946	              throw new TypeError('unsupported type "' + description.type +
1947	                  '"');
1948	          }
1949	          if (stream.getTracks().length) {
1950	            self.remoteStreams.push(stream);
1951	            window.setTimeout(function() {
1952	              var event = new Event('addstream');
1953	              event.stream = stream;
1954	              self.dispatchEvent(event);
1955	              if (self.onaddstream !== null) {
1956	                window.setTimeout(function() {
1957	                  self.onaddstream(event);
1958	                }, 0);
1959	              }
1960
1961	              receiverList.forEach(function(item) {
1962	                var track = item[0];
1963	                var receiver = item[1];
1964	                var trackEvent = new Event('track');
1965	                trackEvent.track = track;
1966	                trackEvent.receiver = receiver;
1967	                trackEvent.streams = [stream];
1968	                self.dispatchEvent(event);
1969	                if (self.ontrack !== null) {
1970	                  window.setTimeout(function() {
1971	                    self.ontrack(trackEvent);
1972	                  }, 0);
1973	                }
1974	              });
1975	            }, 0);
1976	          }
1977	          if (arguments.length > 1 && typeof arguments[1] === 'function') {
1978	            window.setTimeout(arguments[1], 0);
1979	          }
1980	          return Promise.resolve();
1981	        };
1982
1983	    window.RTCPeerConnection.prototype.close = function() {
1984	      this.transceivers.forEach(function(transceiver) {
1985	        /* not yet
1986	        if (transceiver.iceGatherer) {
1987	          transceiver.iceGatherer.close();
1988	        }
1989	        */
1990	        if (transceiver.iceTransport) {
1991	          transceiver.iceTransport.stop();
1992	        }
1993	        if (transceiver.dtlsTransport) {
1994	          transceiver.dtlsTransport.stop();
1995	        }
1996	        if (transceiver.rtpSender) {
1997	          transceiver.rtpSender.stop();
1998	        }
1999	        if (transceiver.rtpReceiver) {
2000	          transceiver.rtpReceiver.stop();
2001	        }
2002	      });
2003	      // FIXME: clean up tracks, local streams, remote streams, etc
2004	      this._updateSignalingState('closed');
2005	    };
2006
2007	    // Update the signaling state.
2008	    window.RTCPeerConnection.prototype._updateSignalingState =
2009	        function(newState) {
2010	          this.signalingState = newState;
2011	          var event = new Event('signalingstatechange');
2012	          this.dispatchEvent(event);
2013	          if (this.onsignalingstatechange !== null) {
2014	            this.onsignalingstatechange(event);
2015	          }
2016	        };
2017
2018	    // Determine whether to fire the negotiationneeded event.
2019	    window.RTCPeerConnection.prototype._maybeFireNegotiationNeeded =
2020	        function() {
2021	          // Fire away (for now).
2022	          var event = new Event('negotiationneeded');
2023	          this.dispatchEvent(event);
2024	          if (this.onnegotiationneeded !== null) {
2025	            this.onnegotiationneeded(event);
2026	          }
2027	        };
2028
2029	    // Update the connection state.
2030	    window.RTCPeerConnection.prototype._updateConnectionState = function() {
2031	      var self = this;
2032	      var newState;
2033	      var states = {
2034	        'new': 0,
2035	        closed: 0,
2036	        connecting: 0,
2037	        checking: 0,
2038	        connected: 0,
2039	        completed: 0,
2040	        failed: 0
2041	      };
2042	      this.transceivers.forEach(function(transceiver) {
2043	        states[transceiver.iceTransport.state]++;
2044	        states[transceiver.dtlsTransport.state]++;
2045	      });
2046	      // ICETransport.completed and connected are the same for this purpose.
2047	      states.connected += states.completed;
2048
2049	      newState = 'new';
2050	      if (states.failed > 0) {
2051	        newState = 'failed';
2052	      } else if (states.connecting > 0 || states.checking > 0) {
2053	        newState = 'connecting';
2054	      } else if (states.disconnected > 0) {
2055	        newState = 'disconnected';
2056	      } else if (states.new > 0) {
2057	        newState = 'new';
2058	      } else if (states.connected > 0 || states.completed > 0) {
2059	        newState = 'connected';
2060	      }
2061
2062	      if (newState !== self.iceConnectionState) {
2063	        self.iceConnectionState = newState;
2064	        var event = new Event('iceconnectionstatechange');
2065	        this.dispatchEvent(event);
2066	        if (this.oniceconnectionstatechange !== null) {
2067	          this.oniceconnectionstatechange(event);
2068	        }
2069	      }
2070	    };
2071
2072	    window.RTCPeerConnection.prototype.createOffer = function() {
2073	      var self = this;
2074	      if (this._pendingOffer) {
2075	        throw new Error('createOffer called while there is a pending offer.');
2076	      }
2077	      var offerOptions;
2078	      if (arguments.length === 1 && typeof arguments[0] !== 'function') {
2079	        offerOptions = arguments[0];
2080	      } else if (arguments.length === 3) {
2081	        offerOptions = arguments[2];
2082	      }
2083
2084	      var tracks = [];
2085	      var numAudioTracks = 0;
2086	      var numVideoTracks = 0;
2087	      // Default to sendrecv.
2088	      if (this.localStreams.length) {
2089	        numAudioTracks = this.localStreams[0].getAudioTracks().length;
2090	        numVideoTracks = this.localStreams[0].getVideoTracks().length;
2091	      }
2092	      // Determine number of audio and video tracks we need to send/recv.
2093	      if (offerOptions) {
2094	        // Reject Chrome legacy constraints.
2095	        if (offerOptions.mandatory || offerOptions.optional) {
2096	          throw new TypeError(
2097	              'Legacy mandatory/optional constraints not supported.');
2098	        }
2099	        if (offerOptions.offerToReceiveAudio !== undefined) {
2100	          numAudioTracks = offerOptions.offerToReceiveAudio;
2101	        }
2102	        if (offerOptions.offerToReceiveVideo !== undefined) {
2103	          numVideoTracks = offerOptions.offerToReceiveVideo;
2104	        }
2105	      }
2106	      if (this.localStreams.length) {
2107	        // Push local streams.
2108	        this.localStreams[0].getTracks().forEach(function(track) {
2109	          tracks.push({
2110	            kind: track.kind,
2111	            track: track,
2112	            wantReceive: track.kind === 'audio' ?
2113	                numAudioTracks > 0 : numVideoTracks > 0
2114	          });
2115	          if (track.kind === 'audio') {
2116	            numAudioTracks--;
2117	          } else if (track.kind === 'video') {
2118	            numVideoTracks--;
2119	          }
2120	        });
2121	      }
2122	      // Create M-lines for recvonly streams.
2123	      while (numAudioTracks > 0 || numVideoTracks > 0) {
2124	        if (numAudioTracks > 0) {
2125	          tracks.push({
2126	            kind: 'audio',
2127	            wantReceive: true
2128	          });
2129	          numAudioTracks--;
2130	        }
2131	        if (numVideoTracks > 0) {
2132	          tracks.push({
2133	            kind: 'video',
2134	            wantReceive: true
2135	          });
2136	          numVideoTracks--;
2137	        }
2138	      }
2139
2140	      var sdp = SDPUtils.writeSessionBoilerplate();
2141	      var transceivers = [];
2142	      tracks.forEach(function(mline, sdpMLineIndex) {
2143	        // For each track, create an ice gatherer, ice transport,
2144	        // dtls transport, potentially rtpsender and rtpreceiver.
2145	        var track = mline.track;
2146	        var kind = mline.kind;
2147	        var mid = SDPUtils.generateIdentifier();
2148
2149	        var transports = self.usingBundle && sdpMLineIndex > 0 ? {
2150	          iceGatherer: transceivers[0].iceGatherer,
2151	          iceTransport: transceivers[0].iceTransport,
2152	          dtlsTransport: transceivers[0].dtlsTransport
2153	        } : self._createIceAndDtlsTransports(mid, sdpMLineIndex);
2154
2155	        var localCapabilities = RTCRtpSender.getCapabilities(kind);
2156	        // filter RTX until additional stuff needed for RTX is implemented
2157	        // in adapter.js
2158	        localCapabilities.codecs = localCapabilities.codecs.filter(
2159	            function(codec) {
2160	              return codec.name !== 'rtx';
2161	            });
2162	        localCapabilities.codecs.forEach(function(codec) {
2163	          // work around https://bugs.chromium.org/p/webrtc/issues/detail?id=6552
2164	          // by adding level-asymmetry-allowed=1
2165	          if (codec.name === 'H264' &&
2166	              codec.parameters['level-asymmetry-allowed'] === undefined) {
2167	            codec.parameters['level-asymmetry-allowed'] = '1';
2168	          }
2169	        });
2170
2171	        var rtpSender;
2172	        var rtpReceiver;
2173
2174	        // generate an ssrc now, to be used later in rtpSender.send
2175	        var sendEncodingParameters = [{
2176	          ssrc: (2 * sdpMLineIndex + 1) * 1001
2177	        }];
2178	        if (track) {
2179	          rtpSender = new RTCRtpSender(track, transports.dtlsTransport);
2180	        }
2181
2182	        if (mline.wantReceive) {
2183	          rtpReceiver = new RTCRtpReceiver(transports.dtlsTransport, kind);
2184	        }
2185
2186	        transceivers[sdpMLineIndex] = {
2187	          iceGatherer: transports.iceGatherer,
2188	          iceTransport: transports.iceTransport,
2189	          dtlsTransport: transports.dtlsTransport,
2190	          localCapabilities: localCapabilities,
2191	          remoteCapabilities: null,
2192	          rtpSender: rtpSender,
2193	          rtpReceiver: rtpReceiver,
2194	          kind: kind,
2195	          mid: mid,
2196	          sendEncodingParameters: sendEncodingParameters,
2197	          recvEncodingParameters: null
2198	        };
2199	      });
2200	      if (this.usingBundle) {
2201	        sdp += 'a=group:BUNDLE ' + transceivers.map(function(t) {
2202	          return t.mid;
2203	        }).join(' ') + '\r\n';
2204	      }
2205	      tracks.forEach(function(mline, sdpMLineIndex) {
2206	        var transceiver = transceivers[sdpMLineIndex];
2207	        sdp += SDPUtils.writeMediaSection(transceiver,
2208	            transceiver.localCapabilities, 'offer', self.localStreams[0]);
2209	      });
2210
2211	      this._pendingOffer = transceivers;
2212	      var desc = new RTCSessionDescription({
2213	        type: 'offer',
2214	        sdp: sdp
2215	      });
2216	      if (arguments.length && typeof arguments[0] === 'function') {
2217	        window.setTimeout(arguments[0], 0, desc);
2218	      }
2219	      return Promise.resolve(desc);
2220	    };
2221
2222	    window.RTCPeerConnection.prototype.createAnswer = function() {
2223	      var self = this;
2224
2225	      var sdp = SDPUtils.writeSessionBoilerplate();
2226	      if (this.usingBundle) {
2227	        sdp += 'a=group:BUNDLE ' + this.transceivers.map(function(t) {
2228	          return t.mid;
2229	        }).join(' ') + '\r\n';
2230	      }
2231	      this.transceivers.forEach(function(transceiver) {
2232	        if (transceiver.isDatachannel) {
2233	          sdp += 'm=application 0 DTLS/SCTP 5000\r\n' +
2234	              'c=IN IP4 0.0.0.0\r\n' +
2235	              'a=mid:' + transceiver.mid + '\r\n';
2236	          return;
2237	        }
2238	        // Calculate intersection of capabilities.
2239	        var commonCapabilities = self._getCommonCapabilities(
2240	            transceiver.localCapabilities,
2241	            transceiver.remoteCapabilities);
2242
2243	        sdp += SDPUtils.writeMediaSection(transceiver, commonCapabilities,
2244	            'answer', self.localStreams[0]);
2245	      });
2246
2247	      var desc = new RTCSessionDescription({
2248	        type: 'answer',
2249	        sdp: sdp
2250	      });
2251	      if (arguments.length && typeof arguments[0] === 'function') {
2252	        window.setTimeout(arguments[0], 0, desc);
2253	      }
2254	      return Promise.resolve(desc);
2255	    };
2256
2257	    window.RTCPeerConnection.prototype.addIceCandidate = function(candidate) {
2258	      if (!candidate) {
2259	        this.transceivers.forEach(function(transceiver) {
2260	          transceiver.iceTransport.addRemoteCandidate({});
2261	        });
2262	      } else {
2263	        var mLineIndex = candidate.sdpMLineIndex;
2264	        if (candidate.sdpMid) {
2265	          for (var i = 0; i < this.transceivers.length; i++) {
2266	            if (this.transceivers[i].mid === candidate.sdpMid) {
2267	              mLineIndex = i;
2268	              break;
2269	            }
2270	          }
2271	        }
2272	        var transceiver = this.transceivers[mLineIndex];
2273	        if (transceiver) {
2274	          var cand = Object.keys(candidate.candidate).length > 0 ?
2275	              SDPUtils.parseCandidate(candidate.candidate) : {};
2276	          // Ignore Chrome's invalid candidates since Edge does not like them.
2277	          if (cand.protocol === 'tcp' && (cand.port === 0 || cand.port === 9)) {
2278	            return;
2279	          }
2280	          // Ignore RTCP candidates, we assume RTCP-MUX.
2281	          if (cand.component !== '1') {
2282	            return;
2283	          }
2284	          // A dirty hack to make samples work.
2285	          if (cand.type === 'endOfCandidates') {
2286	            cand = {};
2287	          }
2288	          transceiver.iceTransport.addRemoteCandidate(cand);
2289
2290	          // update the remoteDescription.
2291	          var sections = SDPUtils.splitSections(this.remoteDescription.sdp);
2292	          sections[mLineIndex + 1] += (cand.type ? candidate.candidate.trim()
2293	              : 'a=end-of-candidates') + '\r\n';
2294	          this.remoteDescription.sdp = sections.join('');
2295	        }
2296	      }
2297	      if (arguments.length > 1 && typeof arguments[1] === 'function') {
2298	        window.setTimeout(arguments[1], 0);
2299	      }
2300	      return Promise.resolve();
2301	    };
2302
2303	    window.RTCPeerConnection.prototype.getStats = function() {
2304	      var promises = [];
2305	      this.transceivers.forEach(function(transceiver) {
2306	        ['rtpSender', 'rtpReceiver', 'iceGatherer', 'iceTransport',
2307	            'dtlsTransport'].forEach(function(method) {
2308	              if (transceiver[method]) {
2309	                promises.push(transceiver[method].getStats());
2310	              }
2311	            });
2312	      });
2313	      var cb = arguments.length > 1 && typeof arguments[1] === 'function' &&
2314	          arguments[1];
2315	      return new Promise(function(resolve) {
2316	        // shim getStats with maplike support
2317	        var results = new Map();
2318	        Promise.all(promises).then(function(res) {
2319	          res.forEach(function(result) {
2320	            Object.keys(result).forEach(function(id) {
2321	              results.set(id, result[id]);
2322	              results[id] = result[id];
2323	            });
2324	          });
2325	          if (cb) {
2326	            window.setTimeout(cb, 0, results);
2327	          }
2328	          resolve(results);
2329	        });
2330	      });
2331	    };
2332	  }
2333	};
2334
2335	// Expose public methods.
2336	module.exports = {
2337	  shimPeerConnection: edgeShim.shimPeerConnection,
2338	  shimGetUserMedia: require('./getusermedia')
2339	};
2340
2341	},{"../utils":10,"./getusermedia":6,"sdp":1}],6:[function(require,module,exports){
2342	/*
2343	 *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
2344	 *
2345	 *  Use of this source code is governed by a BSD-style license
2346	 *  that can be found in the LICENSE file in the root of the source
2347	 *  tree.
2348	 */
2349	 /* eslint-env node */
2350	'use strict';
2351
2352	// Expose public methods.
2353	module.exports = function() {
2354	  var shimError_ = function(e) {
2355	    return {
2356	      name: {PermissionDeniedError: 'NotAllowedError'}[e.name] || e.name,
2357	      message: e.message,
2358	      constraint: e.constraint,
2359	      toString: function() {
2360	        return this.name;
2361	      }
2362	    };
2363	  };
2364
2365	  // getUserMedia error shim.
2366	  var origGetUserMedia = navigator.mediaDevices.getUserMedia.
2367	      bind(navigator.mediaDevices);
2368	  navigator.mediaDevices.getUserMedia = function(c) {
2369	    return origGetUserMedia(c).catch(function(e) {
2370	      return Promise.reject(shimError_(e));
2371	    });
2372	  };
2373	};
2374
2375	},{}],7:[function(require,module,exports){
2376	/*
2377	 *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
2378	 *
2379	 *  Use of this source code is governed by a BSD-style license
2380	 *  that can be found in the LICENSE file in the root of the source
2381	 *  tree.
2382	 */
2383	 /* eslint-env node */
2384	'use strict';
2385
2386	var browserDetails = require('../utils').browserDetails;
2387
2388	var firefoxShim = {
2389	  shimOnTrack: function() {
2390	    if (typeof window === 'object' && window.RTCPeerConnection && !('ontrack' in
2391	        window.RTCPeerConnection.prototype)) {
2392	      Object.defineProperty(window.RTCPeerConnection.prototype, 'ontrack', {
2393	        get: function() {
2394	          return this._ontrack;
2395	        },
2396	        set: function(f) {
2397	          if (this._ontrack) {
2398	            this.removeEventListener('track', this._ontrack);
2399	            this.removeEventListener('addstream', this._ontrackpoly);
2400	          }
2401	          this.addEventListener('track', this._ontrack = f);
2402	          this.addEventListener('addstream', this._ontrackpoly = function(e) {
vendor: 4,232 bytes, lines 2403-2511
2403	            e.stream.getTracks().forEach(function(track) {
2404	              var event = new Event('track');
2405	              event.track = track;
2406	              event.receiver = {track: track};
2407	              event.streams = [e.stream];
2408	              this.dispatchEvent(event);
2409	            }.bind(this));
2410	          }.bind(this));
2411	        }
2412	      });
2413	    }
2414	  },
2415
2416	  shimSourceObject: function() {
2417	    // Firefox has supported mozSrcObject since FF22, unprefixed in 42.
2418	    if (typeof window === 'object') {
2419	      if (window.HTMLMediaElement &&
2420	        !('srcObject' in window.HTMLMediaElement.prototype)) {
2421	        // Shim the srcObject property, once, when HTMLMediaElement is found.
2422	        Object.defineProperty(window.HTMLMediaElement.prototype, 'srcObject', {
2423	          get: function() {
2424	            return this.mozSrcObject;
2425	          },
2426	          set: function(stream) {
2427	            this.mozSrcObject = stream;
2428	          }
2429	        });
2430	      }
2431	    }
2432	  },
2433
2434	  shimPeerConnection: function() {
2435	    if (typeof window !== 'object' || !(window.RTCPeerConnection ||
2436	        window.mozRTCPeerConnection)) {
2437	      return; // probably media.peerconnection.enabled=false in about:config
2438	    }
2439	    // The RTCPeerConnection object.
2440	    if (!window.RTCPeerConnection) {
2441	      window.RTCPeerConnection = function(pcConfig, pcConstraints) {
2442	        if (browserDetails.version < 38) {
2443	          // .urls is not supported in FF < 38.
2444	          // create RTCIceServers with a single url.
2445	          if (pcConfig && pcConfig.iceServers) {
2446	            var newIceServers = [];
2447	            for (var i = 0; i < pcConfig.iceServers.length; i++) {
2448	              var server = pcConfig.iceServers[i];
2449	              if (server.hasOwnProperty('urls')) {
2450	                for (var j = 0; j < server.urls.length; j++) {
2451	                  var newServer = {
2452	                    url: server.urls[j]
2453	                  };
2454	                  if (server.urls[j].indexOf('turn') === 0) {
2455	                    newServer.username = server.username;
2456	                    newServer.credential = server.credential;
2457	                  }
2458	                  newIceServers.push(newServer);
2459	                }
2460	              } else {
2461	                newIceServers.push(pcConfig.iceServers[i]);
2462	              }
2463	            }
2464	            pcConfig.iceServers = newIceServers;
2465	          }
2466	        }
2467	        return new mozRTCPeerConnection(pcConfig, pcConstraints);
2468	      };
2469	      window.RTCPeerConnection.prototype = mozRTCPeerConnection.prototype;
2470
2471	      // wrap static methods. Currently just generateCertificate.
2472	      if (mozRTCPeerConnection.generateCertificate) {
2473	        Object.defineProperty(window.RTCPeerConnection, 'generateCertificate', {
2474	          get: function() {
2475	            return mozRTCPeerConnection.generateCertificate;
2476	          }
2477	        });
2478	      }
2479
2480	      window.RTCSessionDescription = mozRTCSessionDescription;
2481	      window.RTCIceCandidate = mozRTCIceCandidate;
2482	    }
2483
2484	    // shim away need for obsolete RTCIceCandidate/RTCSessionDescription.
2485	    ['setLocalDescription', 'setRemoteDescription', 'addIceCandidate']
2486	        .forEach(function(method) {
2487	          var nativeMethod = RTCPeerConnection.prototype[method];
2488	          RTCPeerConnection.prototype[method] = function() {
2489	            arguments[0] = new ((method === 'addIceCandidate') ?
2490	                RTCIceCandidate : RTCSessionDescription)(arguments[0]);
2491	            return nativeMethod.apply(this, arguments);
2492	          };
2493	        });
2494
2495	    // support for addIceCandidate(null or undefined)
2496	    var nativeAddIceCandidate =
2497	        RTCPeerConnection.prototype.addIceCandidate;
2498	    RTCPeerConnection.prototype.addIceCandidate = function() {
2499	      if (!arguments[0]) {
2500	        if (arguments[1]) {
2501	          arguments[1].apply(null);
2502	        }
2503	        return Promise.resolve();
2504	      }
2505	      return nativeAddIceCandidate.apply(this, arguments);
2506	    };
2507
2508	    if (browserDetails.version < 48) {
2509	      // shim getStats with maplike support
2510	      var makeMapStats = function(stats) {
2511	        var map = new Map();
2512	        Object.keys(stats).forEach(function(key) {
2513	          map.set(key, stats[key]);
2514	          map[key] = stats[key];
2515	        });
2516	        return map;
2517	      };
2518
2519	      var nativeGetStats = RTCPeerConnection.prototype.getStats;
2520	      RTCPeerConnection.prototype.getStats = function(selector, onSucc, onErr) {
2521	        return nativeGetStats.apply(this, [selector || null])
2522	          .then(function(stats) {
2523	            return makeMapStats(stats);
2524	          })
2525	          .then(onSucc, onErr);
2526	      };
2527	    }
2528	  }
2529	};
2530
2531	// Expose public methods.
2532	module.exports = {
2533	  shimOnTrack: firefoxShim.shimOnTrack,
2534	  shimSourceObject: firefoxShim.shimSourceObject,
2535	  shimPeerConnection: firefoxShim.shimPeerConnection,
2536	  shimGetUserMedia: require('./getusermedia')
2537	};
2538
2539	},{"../utils":10,"./getusermedia":8}],8:[function(require,module,exports){
2540	/*
2541	 *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
2542	 *
2543	 *  Use of this source code is governed by a BSD-style license
2544	 *  that can be found in the LICENSE file in the root of the source
2545	 *  tree.
2546	 */
2547	 /* eslint-env node */
2548	'use strict';
2549
2550	var logging = require('../utils').log;
2551	var browserDetails = require('../utils').browserDetails;
2552
2553	// Expose public methods.
2554	module.exports = function() {
2555	  var shimError_ = function(e) {
2556	    return {
2557	      name: {
2558	        SecurityError: 'NotAllowedError',
2559	        PermissionDeniedError: 'NotAllowedError'
2560	      }[e.name] || e.name,
2561	      message: {
2562	        'The operation is insecure.': 'The request is not allowed by the ' +
2563	        'user agent or the platform in the current context.'
2564	      }[e.message] || e.message,
2565	      constraint: e.constraint,
2566	      toString: function() {
2567	        return this.name + (this.message && ': ') + this.message;
2568	      }
2569	    };
2570	  };
2571
2572	  // getUserMedia constraints shim.
2573	  var getUserMedia_ = function(constraints, onSuccess, onError) {
2574	    var constraintsToFF37_ = function(c) {
2575	      if (typeof c !== 'object' || c.require) {
2576	        return c;
2577	      }
2578	      var require = [];
2579	      Object.keys(c).forEach(function(key) {
2580	        if (key === 'require' || key === 'advanced' || key === 'mediaSource') {
2581	          return;
2582	        }
2583	        var r = c[key] = (typeof c[key] === 'object') ?
2584	            c[key] : {ideal: c[key]};
2585	        if (r.min !== undefined ||
2586	            r.max !== undefined || r.exact !== undefined) {
2587	          require.push(key);
2588	        }
2589	        if (r.exact !== undefined) {
2590	          if (typeof r.exact === 'number') {
2591	            r. min = r.max = r.exact;
2592	          } else {
2593	            c[key] = r.exact;
2594	          }
2595	          delete r.exact;
2596	        }
2597	        if (r.ideal !== undefined) {
2598	          c.advanced = c.advanced || [];
2599	          var oc = {};
2600	          if (typeof r.ideal === 'number') {
2601	            oc[key] = {min: r.ideal, max: r.ideal};
2602	          } else {
2603	            oc[key] = r.ideal;
2604	          }
2605	          c.advanced.push(oc);
2606	          delete r.ideal;
2607	          if (!Object.keys(r).length) {
2608	            delete c[key];
2609	          }
2610	        }
2611	      });
2612	      if (require.length) {
2613	        c.require = require;
2614	      }
2615	      return c;
2616	    };
2617	    constraints = JSON.parse(JSON.stringify(constraints));
2618	    if (browserDetails.version < 38) {
2619	      logging('spec: ' + JSON.stringify(constraints));
2620	      if (constraints.audio) {
2621	        constraints.audio = constraintsToFF37_(constraints.audio);
2622	      }
2623	      if (constraints.video) {
2624	        constraints.video = constraintsToFF37_(constraints.video);
2625	      }
2626	      logging('ff37: ' + JSON.stringify(constraints));
2627	    }
2628	    return navigator.mozGetUserMedia(constraints, onSuccess, function(e) {
2629	      onError(shimError_(e));
2630	    });
2631	  };
2632
2633	  // Returns the result of getUserMedia as a Promise.
2634	  var getUserMediaPromise_ = function(constraints) {
2635	    return new Promise(function(resolve, reject) {
2636	      getUserMedia_(constraints, resolve, reject);
2637	    });
2638	  };
2639
2640	  // Shim for mediaDevices on older versions.
2641	  if (!navigator.mediaDevices) {
2642	    navigator.mediaDevices = {getUserMedia: getUserMediaPromise_,
2643	      addEventListener: function() { },
2644	      removeEventListener: function() { }
2645	    };
2646	  }
2647	  navigator.mediaDevices.enumerateDevices =
2648	      navigator.mediaDevices.enumerateDevices || function() {
2649	        return new Promise(function(resolve) {
2650	          var infos = [
2651	            {kind: 'audioinput', deviceId: 'default', label: '', groupId: ''},
2652	            {kind: 'videoinput', deviceId: 'default', label: '', groupId: ''}
2653	          ];
2654	          resolve(infos);
vendor: 6,056 bytes, lines 2655-2829
2655	        });
2656	      };
2657
2658	  if (browserDetails.version < 41) {
2659	    // Work around http://bugzil.la/1169665
2660	    var orgEnumerateDevices =
2661	        navigator.mediaDevices.enumerateDevices.bind(navigator.mediaDevices);
2662	    navigator.mediaDevices.enumerateDevices = function() {
2663	      return orgEnumerateDevices().then(undefined, function(e) {
2664	        if (e.name === 'NotFoundError') {
2665	          return [];
2666	        }
2667	        throw e;
2668	      });
2669	    };
2670	  }
2671	  if (browserDetails.version < 49) {
2672	    var origGetUserMedia = navigator.mediaDevices.getUserMedia.
2673	        bind(navigator.mediaDevices);
2674	    navigator.mediaDevices.getUserMedia = function(c) {
2675	      return origGetUserMedia(c).then(function(stream) {
2676	        // Work around https://bugzil.la/802326
2677	        if (c.audio && !stream.getAudioTracks().length ||
2678	            c.video && !stream.getVideoTracks().length) {
2679	          stream.getTracks().forEach(function(track) {
2680	            track.stop();
2681	          });
2682	          throw new DOMException('The object can not be found here.',
2683	                                 'NotFoundError');
2684	        }
2685	        return stream;
2686	      }, function(e) {
2687	        return Promise.reject(shimError_(e));
2688	      });
2689	    };
2690	  }
2691	  navigator.getUserMedia = function(constraints, onSuccess, onError) {
2692	    if (browserDetails.version < 44) {
2693	      return getUserMedia_(constraints, onSuccess, onError);
2694	    }
2695	    // Replace Firefox 44+'s deprecation warning with unprefixed version.
2696	    console.warn('navigator.getUserMedia has been replaced by ' +
2697	                 'navigator.mediaDevices.getUserMedia');
2698	    navigator.mediaDevices.getUserMedia(constraints).then(onSuccess, onError);
2699	  };
2700	};
2701
2702	},{"../utils":10}],9:[function(require,module,exports){
2703	/*
2704	 *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
2705	 *
2706	 *  Use of this source code is governed by a BSD-style license
2707	 *  that can be found in the LICENSE file in the root of the source
2708	 *  tree.
2709	 */
2710	'use strict';
2711	var safariShim = {
2712	  // TODO: DrAlex, should be here, double check against LayoutTests
2713	  // shimOnTrack: function() { },
2714
2715	  // TODO: once the back-end for the mac port is done, add.
2716	  // TODO: check for webkitGTK+
2717	  // shimPeerConnection: function() { },
2718
2719	  shimGetUserMedia: function() {
2720	    navigator.getUserMedia = navigator.webkitGetUserMedia;
2721	  }
2722	};
2723
2724	// Expose public methods.
2725	module.exports = {
2726	  shimGetUserMedia: safariShim.shimGetUserMedia
2727	  // TODO
2728	  // shimOnTrack: safariShim.shimOnTrack,
2729	  // shimPeerConnection: safariShim.shimPeerConnection
2730	};
2731
2732	},{}],10:[function(require,module,exports){
2733	/*
2734	 *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
2735	 *
2736	 *  Use of this source code is governed by a BSD-style license
2737	 *  that can be found in the LICENSE file in the root of the source
2738	 *  tree.
2739	 */
2740	 /* eslint-env node */
2741	'use strict';
2742
2743	var logDisabled_ = true;
2744
2745	// Utility methods.
2746	var utils = {
2747	  disableLog: function(bool) {
2748	    if (typeof bool !== 'boolean') {
2749	      return new Error('Argument type: ' + typeof bool +
2750	          '. Please use a boolean.');
2751	    }
2752	    logDisabled_ = bool;
2753	    return (bool) ? 'adapter.js logging disabled' :
2754	        'adapter.js logging enabled';
2755	  },
2756
2757	  log: function() {
2758	    if (typeof window === 'object') {
2759	      if (logDisabled_) {
2760	        return;
2761	      }
2762	      if (typeof console !== 'undefined' && typeof console.log === 'function') {
2763	        console.log.apply(console, arguments);
2764	      }
2765	    }
2766	  },
2767
2768	  /**
2769	   * Extract browser version out of the provided user agent string.
2770	   *
2771	   * @param {!string} uastring userAgent string.
2772	   * @param {!string} expr Regular expression used as match criteria.
2773	   * @param {!number} pos position in the version string to be returned.
2774	   * @return {!number} browser version.
2775	   */
2776	  extractVersion: function(uastring, expr, pos) {
2777	    var match = uastring.match(expr);
2778	    return match && match.length >= pos && parseInt(match[pos], 10);
2779	  },
2780
2781	  /**
2782	   * Browser detector.
2783	   *
2784	   * @return {object} result containing browser and version
2785	   *     properties.
2786	   */
2787	  detectBrowser: function() {
2788	    // Returned result object.
2789	    var result = {};
2790	    result.browser = null;
2791	    result.version = null;
2792
2793	    // Fail early if it's not a browser
2794	    if (typeof window === 'undefined' || !window.navigator) {
2795	      result.browser = 'Not a browser.';
2796	      return result;
2797	    }
2798
2799	    // Firefox.
2800	    if (navigator.mozGetUserMedia) {
2801	      result.browser = 'firefox';
2802	      result.version = this.extractVersion(navigator.userAgent,
2803	          /Firefox\/([0-9]+)\./, 1);
2804
2805	    // all webkit-based browsers
2806	    } else if (navigator.webkitGetUserMedia) {
2807	      // Chrome, Chromium, Webview, Opera, all use the chrome shim for now
2808	      if (window.webkitRTCPeerConnection) {
2809	        result.browser = 'chrome';
2810	        result.version = this.extractVersion(navigator.userAgent,
2811	          /Chrom(e|ium)\/([0-9]+)\./, 2);
2812
2813	      // Safari or unknown webkit-based
2814	      // for the time being Safari has support for MediaStreams but not webRTC
2815	      } else {
2816	        // Safari UA substrings of interest for reference:
2817	        // - webkit version:           AppleWebKit/602.1.25 (also used in Op,Cr)
2818	        // - safari UI version:        Version/9.0.3 (unique to Safari)
2819	        // - safari UI webkit version: Safari/601.4.4 (also used in Op,Cr)
2820	        //
2821	        // if the webkit version and safari UI webkit versions are equals,
2822	        // ... this is a stable version.
2823	        //
2824	        // only the internal webkit version is important today to know if
2825	        // media streams are supported
2826	        //
2827	        if (navigator.userAgent.match(/Version\/(\d+).(\d+)/)) {
2828	          result.browser = 'safari';
2829	          result.version = this.extractVersion(navigator.userAgent,
2830	            /AppleWebKit\/([0-9]+)\./, 1);
2831
2832	        // unknown webkit-based browser
2833	        } else {
2834	          result.browser = 'Unsupported webkit-based browser ' +
2835	              'with GUM support but no WebRTC support.';
2836	          return result;
2837	        }
2838	      }
2839
2840	    // Edge.
2841	    } else if (navigator.mediaDevices &&
2842	        navigator.userAgent.match(/Edge\/(\d+).(\d+)$/)) {
2843	      result.browser = 'edge';
2844	      result.version = this.extractVersion(navigator.userAgent,
2845	          /Edge\/(\d+).(\d+)$/, 2);
2846
2847	    // Default fallthrough: not supported.
2848	    } else {
2849	      result.browser = 'Not a supported browser.';
2850	      return result;
2851	    }
2852
2853	    return result;
2854	  }
2855	};
2856
2857	// Export.
2858	module.exports = {
2859	  log: utils.log,
2860	  disableLog: utils.disableLog,
2861	  browserDetails: utils.detectBrowser(),
2862	  extractVersion: utils.extractVersion
2863	};
2864
2865	},{}]},{},[2])(2)
2866	});
2867
2868//enableWebcam();

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.