Browse Source

Updated WebRTC adapter to latest version.

pull/162/head
Simon Eisenmann 11 years ago
parent
commit
e207726148
  1. 10
      static/js/controllers/uicontroller.js
  2. 486
      static/js/libs/webrtc.adapter.js
  3. 13
      static/js/mediastream/usermedia.js
  4. 27
      static/js/services/constraints.js
  5. 4
      static/js/services/mediastream.js

10
static/js/controllers/uicontroller.js

@ -727,15 +727,15 @@ define(['jquery', 'underscore', 'bigscreen', 'moment', 'sjcl', 'modernizr', 'web
}); });
_.defer(function() { _.defer(function() {
if (!Modernizr.websockets) {
alertify.dialog.alert(translation._("Your browser is not supported. Please upgrade to a current version."));
$scope.setStatus("unsupported");
return;
}
if (!$window.webrtcDetectedVersion) { if (!$window.webrtcDetectedVersion) {
alertify.dialog.custom("webrtcUnsupported"); alertify.dialog.custom("webrtcUnsupported");
return; return;
} }
if (!Modernizr.websockets || $window.webrtcDetectedVersion < $window.webrtcMinimumVersion) {
alertify.dialog.alert(translation._("Your browser is not supported. Please upgrade to a current version."));
$scope.setStatus("unsupported");
return;
}
if (mediaStream.config.Renegotiation && $window.webrtcDetectedBrowser === "firefox" && $window.webrtcDetectedVersion < 38) { if (mediaStream.config.Renegotiation && $window.webrtcDetectedBrowser === "firefox" && $window.webrtcDetectedVersion < 38) {
// See https://bugzilla.mozilla.org/show_bug.cgi?id=1017888 // See https://bugzilla.mozilla.org/show_bug.cgi?id=1017888
// and https://bugzilla.mozilla.org/show_bug.cgi?id=840728 // and https://bugzilla.mozilla.org/show_bug.cgi?id=840728

486
static/js/libs/webrtc.adapter.js

@ -30,31 +30,63 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
var RTCPeerConnection = null;
var getUserMedia = null; var getUserMedia = null;
var attachMediaStream = null; var attachMediaStream = null;
var reattachMediaStream = null; var reattachMediaStream = null;
var webrtcDetectedBrowser = null; var webrtcDetectedBrowser = null;
var webrtcDetectedVersion = null; var webrtcDetectedVersion = null;
var webrtcMinimumVersion = null;
var webrtcUtils = {
log: function() {
// suppress console.log output when being included as a module.
if (!(typeof module !== 'undefined' ||
typeof require === 'function') && (typeof define === 'function')) {
console.log.apply(console, arguments);
}
}
};
if (navigator.mozGetUserMedia) { if (typeof window === 'undefined' || !window.navigator) {
console.log('This appears to be Firefox'); webrtcUtils.log('This does not appear to be a browser');
webrtcDetectedBrowser = 'not a browser';
} else if (navigator.mozGetUserMedia) {
webrtcUtils.log('This appears to be Firefox');
webrtcDetectedBrowser = 'firefox'; webrtcDetectedBrowser = 'firefox';
// the detected firefox version.
webrtcDetectedVersion = webrtcDetectedVersion =
parseInt(navigator.userAgent.match(/Firefox\/([0-9]+)\./)[1], 10); parseInt(navigator.userAgent.match(/Firefox\/([0-9]+)\./)[1], 10);
// the minimum firefox version still supported by adapter.
webrtcMinimumVersion = 31;
// The RTCPeerConnection object. // The RTCPeerConnection object.
window.RTCPeerConnection = function(pcConfig, pcConstraints) { window.RTCPeerConnection = function(pcConfig, pcConstraints) {
// .urls is not supported in FF yet. if (webrtcDetectedVersion < 38) {
// .urls is not supported in FF < 38.
// create RTCIceServers with a single url.
if (pcConfig && pcConfig.iceServers) { if (pcConfig && pcConfig.iceServers) {
var newIceServers = [];
for (var i = 0; i < pcConfig.iceServers.length; i++) { for (var i = 0; i < pcConfig.iceServers.length; i++) {
if (pcConfig.iceServers[i].hasOwnProperty('urls')) { var server = pcConfig.iceServers[i];
pcConfig.iceServers[i].url = pcConfig.iceServers[i].urls; if (server.hasOwnProperty('urls')) {
delete pcConfig.iceServers[i].urls; for (var j = 0; j < server.urls.length; j++) {
var newServer = {
url: server.urls[j]
};
if (server.urls[j].indexOf('turn') === 0) {
newServer.username = server.username;
newServer.credential = server.credential;
}
newIceServers.push(newServer);
}
} else {
newIceServers.push(pcConfig.iceServers[i]);
} }
} }
pcConfig.iceServers = newIceServers;
}
} }
return new mozRTCPeerConnection(pcConfig, pcConstraints); return new mozRTCPeerConnection(pcConfig, pcConstraints);
}; };
@ -65,139 +97,407 @@ if (navigator.mozGetUserMedia) {
// The RTCIceCandidate object. // The RTCIceCandidate object.
window.RTCIceCandidate = mozRTCIceCandidate; window.RTCIceCandidate = mozRTCIceCandidate;
// getUserMedia shim (only difference is the prefix). // getUserMedia constraints shim.
// Code from Adam Barth. getUserMedia = function(constraints, onSuccess, onError) {
getUserMedia = navigator.mozGetUserMedia.bind(navigator); var constraintsToFF37 = function(c) {
navigator.getUserMedia = getUserMedia; if (typeof c !== 'object' || c.require) {
return c;
// Creates ICE server from the URL for FF. }
window.createIceServer = function(url, username, password) { var require = [];
var iceServer = null; Object.keys(c).forEach(function(key) {
var urlParts = url.split(':'); if (key === 'require' || key === 'advanced' || key === 'mediaSource') {
if (urlParts[0].indexOf('stun') === 0) { return;
// Create ICE server with STUN URL. }
iceServer = { var r = c[key] = (typeof c[key] === 'object') ?
'url': url c[key] : {ideal: c[key]};
}; if (r.min !== undefined ||
} else if (urlParts[0].indexOf('turn') === 0) { r.max !== undefined || r.exact !== undefined) {
if (webrtcDetectedVersion < 27) { require.push(key);
// Create iceServer with turn url.
// Ignore the transport parameter from TURN url for FF version <=27.
var turnUrlParts = url.split('?');
// Return null for createIceServer if transport=tcp.
if (turnUrlParts.length === 1 ||
turnUrlParts[1].indexOf('transport=udp') === 0) {
iceServer = {
'url': turnUrlParts[0],
'credential': password,
'username': username
};
} }
if (r.exact !== undefined) {
if (typeof r.exact === 'number') {
r.min = r.max = r.exact;
} else { } else {
// FF 27 and above supports transport parameters in TURN url, c[key] = r.exact;
// So passing in the full url to create iceServer. }
iceServer = { delete r.exact;
'url': url, }
'credential': password, if (r.ideal !== undefined) {
'username': username c.advanced = c.advanced || [];
var oc = {};
if (typeof r.ideal === 'number') {
oc[key] = {min: r.ideal, max: r.ideal};
} else {
oc[key] = r.ideal;
}
c.advanced.push(oc);
delete r.ideal;
if (!Object.keys(r).length) {
delete c[key];
}
}
});
if (require.length) {
c.require = require;
}
return c;
}; };
if (webrtcDetectedVersion < 38) {
webrtcUtils.log('spec: ' + JSON.stringify(constraints));
if (constraints.audio) {
constraints.audio = constraintsToFF37(constraints.audio);
}
if (constraints.video) {
constraints.video = constraintsToFF37(constraints.video);
} }
webrtcUtils.log('ff37: ' + JSON.stringify(constraints));
} }
return iceServer; return navigator.mozGetUserMedia(constraints, onSuccess, onError);
}; };
window.createIceServers = function(urls, username, password) { navigator.getUserMedia = getUserMedia;
var iceServers = [];
// Use .url for FireFox. // Shim for mediaDevices on older versions.
for (var i = 0; i < urls.length; i++) { if (!navigator.mediaDevices) {
var iceServer = navigator.mediaDevices = {getUserMedia: requestUserMedia,
window.createIceServer(urls[i], username, password); addEventListener: function() { },
if (iceServer !== null) { removeEventListener: function() { }
iceServers.push(iceServer); };
}
} }
return iceServers; navigator.mediaDevices.enumerateDevices =
navigator.mediaDevices.enumerateDevices || function() {
return new Promise(function(resolve) {
var infos = [
{kind: 'audioinput', deviceId: 'default', label:'', groupId:''},
{kind: 'videoinput', deviceId: 'default', label:'', groupId:''}
];
resolve(infos);
});
}; };
if (webrtcDetectedVersion < 41) {
// Work around http://bugzil.la/1169665
var orgEnumerateDevices =
navigator.mediaDevices.enumerateDevices.bind(navigator.mediaDevices);
navigator.mediaDevices.enumerateDevices = function() {
return orgEnumerateDevices().catch(function(e) {
if (e.name === 'NotFoundError') {
return [];
}
throw e;
});
};
}
// Attach a media stream to an element. // Attach a media stream to an element.
attachMediaStream = function(element, stream) { attachMediaStream = function(element, stream) {
console.log('Attaching media stream');
element.mozSrcObject = stream; element.mozSrcObject = stream;
}; };
reattachMediaStream = function(to, from) { reattachMediaStream = function(to, from) {
console.log('Reattaching media stream');
to.mozSrcObject = from.mozSrcObject; to.mozSrcObject = from.mozSrcObject;
}; };
} else if (navigator.webkitGetUserMedia) { } else if (navigator.webkitGetUserMedia) {
console.log('This appears to be Chrome'); webrtcUtils.log('This appears to be Chrome');
webrtcDetectedBrowser = 'chrome'; webrtcDetectedBrowser = 'chrome';
// Temporary fix until crbug/374263 is fixed.
// Setting Chrome version to 999, if version is unavailable. // the detected chrome version.
var result = navigator.userAgent.match(/Chrom(e|ium)\/([0-9]+)\./); webrtcDetectedVersion =
if (result !== null) { parseInt(navigator.userAgent.match(/Chrom(e|ium)\/([0-9]+)\./)[2], 10);
webrtcDetectedVersion = parseInt(result[2], 10);
} else { // the minimum chrome version still supported by adapter.
webrtcDetectedVersion = 999; webrtcMinimumVersion = 38;
// The RTCPeerConnection object.
window.RTCPeerConnection = function(pcConfig, pcConstraints) {
var pc = new webkitRTCPeerConnection(pcConfig, pcConstraints);
var origGetStats = pc.getStats.bind(pc);
pc.getStats = function(selector, successCallback, errorCallback) { // jshint ignore: line
var self = this;
var args = arguments;
// If selector is a function then we are in the old style stats so just
// pass back the original getStats format to avoid breaking old users.
if (arguments.length > 0 && typeof selector === 'function') {
return origGetStats(selector, successCallback);
} }
// Creates iceServer from the url for Chrome M33 and earlier. var fixChromeStats = function(response) {
window.createIceServer = function(url, username, password) { var standardReport = {};
var iceServer = null; var reports = response.result();
var urlParts = url.split(':'); reports.forEach(function(report) {
if (urlParts[0].indexOf('stun') === 0) { var standardStats = {
// Create iceServer with stun url. id: report.id,
iceServer = { timestamp: report.timestamp,
'url': url type: report.type
};
report.names().forEach(function(name) {
standardStats[name] = report.stat(name);
});
standardReport[standardStats.id] = standardStats;
});
return standardReport;
}; };
} else if (urlParts[0].indexOf('turn') === 0) {
// Chrome M28 & above uses below TURN format. if (arguments.length >= 2) {
iceServer = { var successCallbackWrapper = function(response) {
'url': url, args[1](fixChromeStats(response));
'credential': password,
'username': username
}; };
return origGetStats.apply(this, [successCallbackWrapper, arguments[0]]);
} }
return iceServer;
// promise-support
return new Promise(function(resolve, reject) {
origGetStats.apply(self, [resolve, reject]);
});
}; };
// Creates an ICEServer object from multiple URLs. return pc;
window.createIceServers = function(urls, username, password) {
return [{
'urls': urls,
'credential': password,
'username': username
}];
}; };
// The RTCPeerConnection object. // add promise support
RTCPeerConnection = function(pcConfig, pcConstraints) { ['createOffer', 'createAnswer'].forEach(function(method) {
return new webkitRTCPeerConnection(pcConfig, pcConstraints); var nativeMethod = webkitRTCPeerConnection.prototype[method];
webkitRTCPeerConnection.prototype[method] = function() {
var self = this;
if (arguments.length < 1 || (arguments.length === 1 &&
typeof(arguments[0]) === 'object')) {
var opts = arguments.length === 1 ? arguments[0] : undefined;
return new Promise(function(resolve, reject) {
nativeMethod.apply(self, [resolve, reject, opts]);
});
} else {
return nativeMethod.apply(this, arguments);
}
};
});
['setLocalDescription', 'setRemoteDescription',
'addIceCandidate'].forEach(function(method) {
var nativeMethod = webkitRTCPeerConnection.prototype[method];
webkitRTCPeerConnection.prototype[method] = function() {
var args = arguments;
var self = this;
return new Promise(function(resolve, reject) {
nativeMethod.apply(self, [args[0],
function() {
resolve();
if (args.length >= 2) {
args[1].apply(null, []);
}
},
function(err) {
reject(err);
if (args.length >= 3) {
args[2].apply(null, [err]);
}
}]
);
});
};
});
// getUserMedia constraints shim.
var constraintsToChrome = function(c) {
if (typeof c !== 'object' || c.mandatory || c.optional) {
return c;
}
var cc = {};
Object.keys(c).forEach(function(key) {
if (key === 'require' || key === 'advanced' || key === 'mediaSource') {
return;
}
var r = (typeof c[key] === 'object') ? c[key] : {ideal: c[key]};
if (r.exact !== undefined && typeof r.exact === 'number') {
r.min = r.max = r.exact;
}
var oldname = function(prefix, name) {
if (prefix) {
return prefix + name.charAt(0).toUpperCase() + name.slice(1);
}
return (name === 'deviceId') ? 'sourceId' : name;
};
if (r.ideal !== undefined) {
cc.optional = cc.optional || [];
var oc = {};
if (typeof r.ideal === 'number') {
oc[oldname('min', key)] = r.ideal;
cc.optional.push(oc);
oc = {};
oc[oldname('max', key)] = r.ideal;
cc.optional.push(oc);
} else {
oc[oldname('', key)] = r.ideal;
cc.optional.push(oc);
}
}
if (r.exact !== undefined && typeof r.exact !== 'number') {
cc.mandatory = cc.mandatory || {};
cc.mandatory[oldname('', key)] = r.exact;
} else {
['min', 'max'].forEach(function(mix) {
if (r[mix] !== undefined) {
cc.mandatory = cc.mandatory || {};
cc.mandatory[oldname(mix, key)] = r[mix];
}
});
}
});
if (c.advanced) {
cc.optional = (cc.optional || []).concat(c.advanced);
}
return cc;
}; };
// Get UserMedia (only difference is the prefix). getUserMedia = function(constraints, onSuccess, onError) {
// Code from Adam Barth. if (constraints.audio) {
getUserMedia = navigator.webkitGetUserMedia.bind(navigator); constraints.audio = constraintsToChrome(constraints.audio);
}
if (constraints.video) {
constraints.video = constraintsToChrome(constraints.video);
}
webrtcUtils.log('chrome: ' + JSON.stringify(constraints));
return navigator.webkitGetUserMedia(constraints, onSuccess, onError);
};
navigator.getUserMedia = getUserMedia; navigator.getUserMedia = getUserMedia;
if (!navigator.mediaDevices) {
navigator.mediaDevices = {getUserMedia: requestUserMedia,
enumerateDevices: function() {
return new Promise(function(resolve) {
var kinds = {audio: 'audioinput', video: 'videoinput'};
return MediaStreamTrack.getSources(function(devices) {
resolve(devices.map(function(device) {
return {label: device.label,
kind: kinds[device.kind],
deviceId: device.id,
groupId: ''};
}));
});
});
}};
}
// A shim for getUserMedia method on the mediaDevices object.
// TODO(KaptenJansson) remove once implemented in Chrome stable.
if (!navigator.mediaDevices.getUserMedia) {
navigator.mediaDevices.getUserMedia = function(constraints) {
return requestUserMedia(constraints);
};
} else {
// Even though Chrome 45 has navigator.mediaDevices and a getUserMedia
// function which returns a Promise, it does not accept spec-style
// constraints.
var origGetUserMedia = navigator.mediaDevices.getUserMedia.
bind(navigator.mediaDevices);
navigator.mediaDevices.getUserMedia = function(c) {
webrtcUtils.log('spec: ' + JSON.stringify(c)); // whitespace for alignment
c.audio = constraintsToChrome(c.audio);
c.video = constraintsToChrome(c.video);
webrtcUtils.log('chrome: ' + JSON.stringify(c));
return origGetUserMedia(c);
};
}
// Dummy devicechange event methods.
// TODO(KaptenJansson) remove once implemented in Chrome stable.
if (typeof navigator.mediaDevices.addEventListener === 'undefined') {
navigator.mediaDevices.addEventListener = function() {
webrtcUtils.log('Dummy mediaDevices.addEventListener called.');
};
}
if (typeof navigator.mediaDevices.removeEventListener === 'undefined') {
navigator.mediaDevices.removeEventListener = function() {
webrtcUtils.log('Dummy mediaDevices.removeEventListener called.');
};
}
// Attach a media stream to an element. // Attach a media stream to an element.
attachMediaStream = function(element, stream) { attachMediaStream = function(element, stream) {
if (typeof element.srcObject !== 'undefined') { if (typeof element.srcObject !== 'undefined') {
element.srcObject = stream; element.srcObject = stream;
} else if (typeof element.mozSrcObject !== 'undefined') {
element.mozSrcObject = stream;
} else if (typeof element.src !== 'undefined') { } else if (typeof element.src !== 'undefined') {
element.src = URL.createObjectURL(stream); element.src = URL.createObjectURL(stream);
} else { } else {
console.log('Error attaching stream to element.'); webrtcUtils.log('Error attaching stream to element.');
} }
}; };
reattachMediaStream = function(to, from) { reattachMediaStream = function(to, from) {
to.src = from.src; to.src = from.src;
}; };
} else if (navigator.mediaDevices && navigator.userAgent.match(
/Edge\/(\d+).(\d+)$/)) {
webrtcUtils.log('This appears to be Edge');
webrtcDetectedBrowser = 'edge';
webrtcDetectedVersion =
parseInt(navigator.userAgent.match(/Edge\/(\d+).(\d+)$/)[2], 10);
// the minimum version still supported by adapter.
webrtcMinimumVersion = 12;
getUserMedia = navigator.getUserMedia;
attachMediaStream = function(element, stream) {
element.srcObject = stream;
};
reattachMediaStream = function(to, from) {
to.srcObject = from.srcObject;
};
} else { } else {
console.log('Browser does not appear to be WebRTC-capable'); webrtcUtils.log('Browser does not appear to be WebRTC-capable');
}
// Returns the result of getUserMedia as a Promise.
function requestUserMedia(constraints) {
return new Promise(function(resolve, reject) {
getUserMedia(constraints, resolve, reject);
});
}
var webrtcTesting = {};
Object.defineProperty(webrtcTesting, 'version', {
set: function(version) {
webrtcDetectedVersion = version;
}
});
if (typeof module !== 'undefined') {
var RTCPeerConnection;
if (typeof window !== 'undefined') {
RTCPeerConnection = window.RTCPeerConnection;
}
module.exports = {
RTCPeerConnection: RTCPeerConnection,
getUserMedia: getUserMedia,
attachMediaStream: attachMediaStream,
reattachMediaStream: reattachMediaStream,
webrtcDetectedBrowser: webrtcDetectedBrowser,
webrtcDetectedVersion: webrtcDetectedVersion,
webrtcMinimumVersion: webrtcMinimumVersion,
webrtcTesting: webrtcTesting
//requestUserMedia: not exposed on purpose.
//trace: not exposed on purpose.
};
} else if ((typeof require === 'function') && (typeof define === 'function')) {
// Expose objects and functions when RequireJS is doing the loading.
define([], function() {
return {
RTCPeerConnection: window.RTCPeerConnection,
getUserMedia: getUserMedia,
attachMediaStream: attachMediaStream,
reattachMediaStream: reattachMediaStream,
webrtcDetectedBrowser: webrtcDetectedBrowser,
webrtcDetectedVersion: webrtcDetectedVersion,
webrtcMinimumVersion: webrtcMinimumVersion,
webrtcTesting: webrtcTesting
//requestUserMedia: not exposed on purpose.
//trace: not exposed on purpose.
};
});
} }

13
static/js/mediastream/usermedia.js

@ -83,19 +83,6 @@ define(['jquery', 'underscore', 'audiocontext', 'mediastream/dummystream', 'webr
} }
} }
}); });
if (window.webrtcDetectedBrowser === "firefox" && window.webrtcDetectedVersion < 38) {
// Firefox < 38 needs a extra require field.
var r = [];
if (c.height) {
r.push("height");
}
if (c.width) {
r.push("width");
}
if (r.length) {
c.require = r;
}
}
return c; return c;
}; };
// Adapter to support navigator.mediaDevices API. // Adapter to support navigator.mediaDevices API.

27
static/js/services/constraints.js

@ -128,23 +128,24 @@
}; };
service.iceServers = function(constraints) { service.iceServers = function(constraints) {
var createIceServers = function(urls, username, password) {
var s = {
urls: urls
}
if (username) {
s.username = username;
s.credential = password;
}
return s;
};
var iceServers = []; var iceServers = [];
var iceServer;
if (service.stun && service.stun.length) { if (service.stun && service.stun.length) {
iceServer = $window.createIceServers(service.stun); iceServers.push(createIceServers(service.stun));
if (iceServer.length) {
iceServers.push.apply(iceServers, iceServer)
}
} }
if (service.turn && service.turn.urls && service.turn.urls.length) { if (service.turn && service.turn.urls && service.turn.urls.length) {
iceServer = $window.createIceServers(service.turn.urls, service.turn.username, service.turn.password); iceServers.push(createIceServers(service.turn.urls, service.turn.username, service.turn.password));
if (iceServer.length) {
iceServers.push.apply(iceServers, iceServer)
}
} }
webrtc.settings.pcConfig.iceServers = iceServers; webrtc.settings.pcConfig.iceServers = iceServers;
}; };
// Some default constraints. // Some default constraints.
@ -191,6 +192,7 @@
supported: (function() { supported: (function() {
var isChrome = $window.webrtcDetectedBrowser === "chrome"; var isChrome = $window.webrtcDetectedBrowser === "chrome";
var isFirefox = $window.webrtcDetectedBrowser === "firefox"; var isFirefox = $window.webrtcDetectedBrowser === "firefox";
var isEdge = $window.webrtcDetectedBrowser === "edge";
var version = $window.webrtcDetectedVersion; var version = $window.webrtcDetectedVersion;
// Constraints support table. // Constraints support table.
return { return {
@ -201,7 +203,8 @@
// Chrome supports this on Windows only. // Chrome supports this on Windows only.
renderToAssociatedSink: isChrome && $window.navigator.platform.indexOf("Win") === 0, renderToAssociatedSink: isChrome && $window.navigator.platform.indexOf("Win") === 0,
chrome: isChrome, chrome: isChrome,
firefox: isFirefox firefox: isFirefox,
edge: isEdge
}; };
})() })()
}; };

4
static/js/services/mediastream.js

@ -46,8 +46,8 @@ define([
// Apply configuration details. // Apply configuration details.
webrtc.settings.renegotiation = context.Cfg.Renegotiation && true; webrtc.settings.renegotiation = context.Cfg.Renegotiation && true;
if (webrtc.settings.renegotiation && $window.webrtcDetectedBrowser === "firefox") { if (webrtc.settings.renegotiation && $window.webrtcDetectedBrowser !== "chrome") {
console.warn("Disable renegotiation in Firefox for now."); console.warn("Disable renegotiation in anything but Chrome for now.");
webrtc.settings.renegotiation = false; webrtc.settings.renegotiation = false;
} }

Loading…
Cancel
Save