Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
beefproject
GitHub Repository: beefproject/beef
Path: blob/master/core/main/client/lib/webrtcadapter.js
1154 views
1
/*
2
* Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
3
*
4
* Use of this source code is governed by a BSD-style license
5
* that can be found in the LICENSE file in the root of the source
6
* tree.
7
*/
8
9
/* More information about these options at jshint.com/docs/options */
10
/* jshint browser: true, camelcase: true, curly: true, devel: true,
11
eqeqeq: true, forin: false, globalstrict: true, node: true,
12
quotmark: single, undef: true, unused: strict */
13
/* global mozRTCIceCandidate, mozRTCPeerConnection, Promise,
14
mozRTCSessionDescription, webkitRTCPeerConnection, MediaStreamTrack */
15
/* exported trace,requestUserMedia */
16
17
'use strict';
18
19
var getUserMedia = null;
20
var attachMediaStream = null;
21
var reattachMediaStream = null;
22
var webrtcDetectedBrowser = null;
23
var webrtcDetectedVersion = null;
24
var webrtcMinimumVersion = null;
25
26
function trace(text) {
27
// This function is used for logging.
28
if (text[text.length - 1] === '\n') {
29
text = text.substring(0, text.length - 1);
30
}
31
if (window.performance) {
32
var now = (window.performance.now() / 1000).toFixed(3);
33
beef.debug(now + ': ' + text);
34
} else {
35
beef.debug(text);
36
}
37
}
38
39
if (navigator.mozGetUserMedia) {
40
41
webrtcDetectedBrowser = 'firefox';
42
43
// the detected firefox version.
44
webrtcDetectedVersion =
45
parseInt(navigator.userAgent.match(/Firefox\/([0-9]+)\./)[1], 10);
46
47
// the minimum firefox version still supported by adapter.
48
webrtcMinimumVersion = 31;
49
50
// The RTCPeerConnection object.
51
window.RTCPeerConnection = function(pcConfig, pcConstraints) {
52
if (webrtcDetectedVersion < 38) {
53
// .urls is not supported in FF < 38.
54
// create RTCIceServers with a single url.
55
if (pcConfig && pcConfig.iceServers) {
56
var newIceServers = [];
57
for (var i = 0; i < pcConfig.iceServers.length; i++) {
58
var server = pcConfig.iceServers[i];
59
if (server.hasOwnProperty('urls')) {
60
for (var j = 0; j < server.urls.length; j++) {
61
var newServer = {
62
url: server.urls[j]
63
};
64
if (server.urls[j].indexOf('turn') === 0) {
65
newServer.username = server.username;
66
newServer.credential = server.credential;
67
}
68
newIceServers.push(newServer);
69
}
70
} else {
71
newIceServers.push(pcConfig.iceServers[i]);
72
}
73
}
74
pcConfig.iceServers = newIceServers;
75
}
76
}
77
return new mozRTCPeerConnection(pcConfig, pcConstraints);
78
};
79
80
try {
81
// The RTCSessionDescription object.
82
window.RTCSessionDescription = mozRTCSessionDescription;
83
84
// The RTCIceCandidate object.
85
window.RTCIceCandidate = mozRTCIceCandidate;
86
87
}catch(err) {
88
89
}
90
91
// getUserMedia constraints shim.
92
getUserMedia = (webrtcDetectedVersion < 38) ?
93
function(c, onSuccess, onError) {
94
var constraintsToFF37 = function(c) {
95
if (typeof c !== 'object' || c.require) {
96
return c;
97
}
98
var require = [];
99
Object.keys(c).forEach(function(key) {
100
var r = c[key] = (typeof c[key] === 'object') ?
101
c[key] : {ideal: c[key]};
102
if (r.exact !== undefined) {
103
r.min = r.max = r.exact;
104
delete r.exact;
105
}
106
if (r.min !== undefined || r.max !== undefined) {
107
require.push(key);
108
}
109
if (r.ideal !== undefined) {
110
c.advanced = c.advanced || [];
111
var oc = {};
112
oc[key] = {min: r.ideal, max: r.ideal};
113
c.advanced.push(oc);
114
delete r.ideal;
115
if (!Object.keys(r).length) {
116
delete c[key];
117
}
118
}
119
});
120
if (require.length) {
121
c.require = require;
122
}
123
return c;
124
};
125
beef.debug('spec: ' + JSON.stringify(c));
126
c.audio = constraintsToFF37(c.audio);
127
c.video = constraintsToFF37(c.video);
128
beef.debug('ff37: ' + JSON.stringify(c));
129
return navigator.mozGetUserMedia(c, onSuccess, onError);
130
} : navigator.mozGetUserMedia.bind(navigator);
131
132
navigator.getUserMedia = getUserMedia;
133
134
// Shim for mediaDevices on older versions.
135
if (!navigator.mediaDevices) {
136
navigator.mediaDevices = {getUserMedia: requestUserMedia,
137
addEventListener: function() { },
138
removeEventListener: function() { }
139
};
140
}
141
navigator.mediaDevices.enumerateDevices =
142
navigator.mediaDevices.enumerateDevices || function() {
143
return new Promise(function(resolve) {
144
var infos = [
145
{kind: 'audioinput', deviceId: 'default', label:'', groupId:''},
146
{kind: 'videoinput', deviceId: 'default', label:'', groupId:''}
147
];
148
resolve(infos);
149
});
150
};
151
152
if (webrtcDetectedVersion < 41) {
153
// Work around http://bugzil.la/1169665
154
var orgEnumerateDevices =
155
navigator.mediaDevices.enumerateDevices.bind(navigator.mediaDevices);
156
navigator.mediaDevices.enumerateDevices = function() {
157
return orgEnumerateDevices().then(undefined, function(e) {
158
if (e.name === 'NotFoundError') {
159
return [];
160
}
161
throw e;
162
});
163
};
164
}
165
// Attach a media stream to an element.
166
attachMediaStream = function(element, stream) {
167
beef.debug('Attaching media stream');
168
element.mozSrcObject = stream;
169
};
170
171
reattachMediaStream = function(to, from) {
172
beef.debug('Reattaching media stream');
173
to.mozSrcObject = from.mozSrcObject;
174
};
175
176
} else if (navigator.webkitGetUserMedia) {
177
178
webrtcDetectedBrowser = 'chrome';
179
180
// the detected chrome version.
181
webrtcDetectedVersion =
182
parseInt(navigator.userAgent.match(/Chrom(e|ium)\/([0-9]+)\./)[2], 10);
183
184
// the minimum chrome version still supported by adapter.
185
webrtcMinimumVersion = 38;
186
187
// The RTCPeerConnection object.
188
window.RTCPeerConnection = function(pcConfig, pcConstraints) {
189
var pc = new webkitRTCPeerConnection(pcConfig, pcConstraints);
190
var origGetStats = pc.getStats.bind(pc);
191
pc.getStats = function(selector, successCallback, errorCallback) { // jshint ignore: line
192
// If selector is a function then we are in the old style stats so just
193
// pass back the original getStats format to avoid breaking old users.
194
if (typeof selector === 'function') {
195
return origGetStats(selector, successCallback);
196
}
197
198
var fixChromeStats = function(response) {
199
var standardReport = {};
200
var reports = response.result();
201
reports.forEach(function(report) {
202
var standardStats = {
203
id: report.id,
204
timestamp: report.timestamp,
205
type: report.type
206
};
207
report.names().forEach(function(name) {
208
standardStats[name] = report.stat(name);
209
});
210
standardReport[standardStats.id] = standardStats;
211
});
212
213
return standardReport;
214
};
215
var successCallbackWrapper = function(response) {
216
successCallback(fixChromeStats(response));
217
};
218
return origGetStats(successCallbackWrapper, selector);
219
};
220
221
return pc;
222
};
223
224
// add promise support
225
['createOffer', 'createAnswer'].forEach(function(method) {
226
var nativeMethod = webkitRTCPeerConnection.prototype[method];
227
webkitRTCPeerConnection.prototype[method] = function() {
228
var self = this;
229
if (arguments.length < 1 || (arguments.length === 1 &&
230
typeof(arguments[0]) === 'object')) {
231
var opts = arguments.length === 1 ? arguments[0] : undefined;
232
return new Promise(function(resolve, reject) {
233
nativeMethod.apply(self, [resolve, reject, opts]);
234
});
235
} else {
236
return nativeMethod.apply(this, arguments);
237
}
238
};
239
});
240
241
['setLocalDescription', 'setRemoteDescription',
242
'addIceCandidate'].forEach(function(method) {
243
var nativeMethod = webkitRTCPeerConnection.prototype[method];
244
webkitRTCPeerConnection.prototype[method] = function() {
245
var args = arguments;
246
var self = this;
247
return new Promise(function(resolve, reject) {
248
nativeMethod.apply(self, [args[0],
249
function() {
250
resolve();
251
if (args.length >= 2) {
252
args[1].apply(null, []);
253
}
254
},
255
function(err) {
256
reject(err);
257
if (args.length >= 3) {
258
args[2].apply(null, [err]);
259
}
260
}]
261
);
262
});
263
};
264
});
265
266
// getUserMedia constraints shim.
267
getUserMedia = function(c, onSuccess, onError) {
268
var constraintsToChrome = function(c) {
269
if (typeof c !== 'object' || c.mandatory || c.optional) {
270
return c;
271
}
272
var cc = {};
273
Object.keys(c).forEach(function(key) {
274
if (key === 'require' || key === 'advanced') {
275
return;
276
}
277
var r = (typeof c[key] === 'object') ? c[key] : {ideal: c[key]};
278
if (r.exact !== undefined && typeof r.exact === 'number') {
279
r.min = r.max = r.exact;
280
}
281
var oldname = function(prefix, name) {
282
if (prefix) {
283
return prefix + name.charAt(0).toUpperCase() + name.slice(1);
284
}
285
return (name === 'deviceId') ? 'sourceId' : name;
286
};
287
if (r.ideal !== undefined) {
288
cc.optional = cc.optional || [];
289
var oc = {};
290
if (typeof r.ideal === 'number') {
291
oc[oldname('min', key)] = r.ideal;
292
cc.optional.push(oc);
293
oc = {};
294
oc[oldname('max', key)] = r.ideal;
295
cc.optional.push(oc);
296
} else {
297
oc[oldname('', key)] = r.ideal;
298
cc.optional.push(oc);
299
}
300
}
301
if (r.exact !== undefined && typeof r.exact !== 'number') {
302
cc.mandatory = cc.mandatory || {};
303
cc.mandatory[oldname('', key)] = r.exact;
304
} else {
305
['min', 'max'].forEach(function(mix) {
306
if (r[mix] !== undefined) {
307
cc.mandatory = cc.mandatory || {};
308
cc.mandatory[oldname(mix, key)] = r[mix];
309
}
310
});
311
}
312
});
313
if (c.advanced) {
314
cc.optional = (cc.optional || []).concat(c.advanced);
315
}
316
return cc;
317
};
318
beef.debug('spec: ' + JSON.stringify(c)); // whitespace for alignment
319
c.audio = constraintsToChrome(c.audio);
320
c.video = constraintsToChrome(c.video);
321
beef.debug('chrome: ' + JSON.stringify(c));
322
return navigator.webkitGetUserMedia(c, onSuccess, onError);
323
};
324
navigator.getUserMedia = getUserMedia;
325
326
// Attach a media stream to an element.
327
attachMediaStream = function(element, stream) {
328
if (typeof element.srcObject !== 'undefined') {
329
element.srcObject = stream;
330
} else if (typeof element.src !== 'undefined') {
331
element.src = URL.createObjectURL(stream);
332
} else {
333
beef.debug('Error attaching stream to element.');
334
}
335
};
336
337
reattachMediaStream = function(to, from) {
338
to.src = from.src;
339
};
340
341
if (!navigator.mediaDevices) {
342
navigator.mediaDevices = {getUserMedia: requestUserMedia,
343
enumerateDevices: function() {
344
return new Promise(function(resolve) {
345
var kinds = {audio: 'audioinput', video: 'videoinput'};
346
return MediaStreamTrack.getSources(function(devices) {
347
resolve(devices.map(function(device) {
348
return {label: device.label,
349
kind: kinds[device.kind],
350
deviceId: device.id,
351
groupId: ''};
352
}));
353
});
354
});
355
}};
356
// in case someone wants to listen for the devicechange event.
357
navigator.mediaDevices.addEventListener = function() { };
358
navigator.mediaDevices.removeEventListener = function() { };
359
}
360
} else if (navigator.mediaDevices && navigator.userAgent.match(
361
/Edge\/(\d+).(\d+)$/)) {
362
webrtcDetectedBrowser = 'edge';
363
364
webrtcDetectedVersion =
365
parseInt(navigator.userAgent.match(/Edge\/(\d+).(\d+)$/)[2], 10);
366
367
// the minimum version still supported by adapter.
368
webrtcMinimumVersion = 12;
369
370
attachMediaStream = function(element, stream) {
371
element.srcObject = stream;
372
};
373
reattachMediaStream = function(to, from) {
374
to.srcObject = from.srcObject;
375
};
376
} else {
377
// console.log('Browser does not appear to be WebRTC-capable');
378
}
379
380
// Returns the result of getUserMedia as a Promise.
381
function requestUserMedia(constraints) {
382
return new Promise(function(resolve, reject) {
383
getUserMedia(constraints, resolve, reject);
384
});
385
}
386
387
if (typeof module !== 'undefined') {
388
module.exports = {
389
RTCPeerConnection: window.RTCPeerConnection,
390
getUserMedia: getUserMedia,
391
attachMediaStream: attachMediaStream,
392
reattachMediaStream: reattachMediaStream,
393
webrtcDetectedBrowser: webrtcDetectedBrowser,
394
webrtcDetectedVersion: webrtcDetectedVersion,
395
webrtcMinimumVersion: webrtcMinimumVersion
396
//requestUserMedia: not exposed on purpose.
397
//trace: not exposed on purpose.
398
};
399
} else if ((typeof require === 'function') && (typeof define === 'function')) {
400
// Expose objects and functions when RequireJS is doing the loading.
401
define([], function() {
402
return {
403
RTCPeerConnection: window.RTCPeerConnection,
404
getUserMedia: getUserMedia,
405
attachMediaStream: attachMediaStream,
406
reattachMediaStream: reattachMediaStream,
407
webrtcDetectedBrowser: webrtcDetectedBrowser,
408
webrtcDetectedVersion: webrtcDetectedVersion,
409
webrtcMinimumVersion: webrtcMinimumVersion
410
//requestUserMedia: not exposed on purpose.
411
//trace: not exposed on purpose.
412
};
413
});
414
}
415
416