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. 502
      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 @@ -727,15 +727,15 @@ define(['jquery', 'underscore', 'bigscreen', 'moment', 'sjcl', 'modernizr', 'web
});
_.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) {
alertify.dialog.custom("webrtcUnsupported");
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) {
// See https://bugzilla.mozilla.org/show_bug.cgi?id=1017888
// and https://bugzilla.mozilla.org/show_bug.cgi?id=840728

502
static/js/libs/webrtc.adapter.js

@ -30,30 +30,62 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT @@ -30,30 +30,62 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
var RTCPeerConnection = null;
var getUserMedia = null;
var attachMediaStream = null;
var reattachMediaStream = null;
var webrtcDetectedBrowser = 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) {
console.log('This appears to be Firefox');
if (typeof window === 'undefined' || !window.navigator) {
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';
// the detected firefox version.
webrtcDetectedVersion =
parseInt(navigator.userAgent.match(/Firefox\/([0-9]+)\./)[1], 10);
// the minimum firefox version still supported by adapter.
webrtcMinimumVersion = 31;
// The RTCPeerConnection object.
window.RTCPeerConnection = function(pcConfig, pcConstraints) {
// .urls is not supported in FF yet.
if (pcConfig && pcConfig.iceServers) {
for (var i = 0; i < pcConfig.iceServers.length; i++) {
if (pcConfig.iceServers[i].hasOwnProperty('urls')) {
pcConfig.iceServers[i].url = pcConfig.iceServers[i].urls;
delete pcConfig.iceServers[i].urls;
if (webrtcDetectedVersion < 38) {
// .urls is not supported in FF < 38.
// create RTCIceServers with a single url.
if (pcConfig && pcConfig.iceServers) {
var newIceServers = [];
for (var i = 0; i < pcConfig.iceServers.length; i++) {
var server = pcConfig.iceServers[i];
if (server.hasOwnProperty('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);
@ -65,139 +97,407 @@ if (navigator.mozGetUserMedia) { @@ -65,139 +97,407 @@ if (navigator.mozGetUserMedia) {
// The RTCIceCandidate object.
window.RTCIceCandidate = mozRTCIceCandidate;
// getUserMedia shim (only difference is the prefix).
// Code from Adam Barth.
getUserMedia = navigator.mozGetUserMedia.bind(navigator);
navigator.getUserMedia = getUserMedia;
// Creates ICE server from the URL for FF.
window.createIceServer = function(url, username, password) {
var iceServer = null;
var urlParts = url.split(':');
if (urlParts[0].indexOf('stun') === 0) {
// Create ICE server with STUN URL.
iceServer = {
'url': url
};
} else if (urlParts[0].indexOf('turn') === 0) {
if (webrtcDetectedVersion < 27) {
// 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
};
// getUserMedia constraints shim.
getUserMedia = function(constraints, onSuccess, onError) {
var constraintsToFF37 = function(c) {
if (typeof c !== 'object' || c.require) {
return c;
}
var require = [];
Object.keys(c).forEach(function(key) {
if (key === 'require' || key === 'advanced' || key === 'mediaSource') {
return;
}
} else {
// FF 27 and above supports transport parameters in TURN url,
// So passing in the full url to create iceServer.
iceServer = {
'url': url,
'credential': password,
'username': username
};
var r = c[key] = (typeof c[key] === 'object') ?
c[key] : {ideal: c[key]};
if (r.min !== undefined ||
r.max !== undefined || r.exact !== undefined) {
require.push(key);
}
if (r.exact !== undefined) {
if (typeof r.exact === 'number') {
r.min = r.max = r.exact;
} else {
c[key] = r.exact;
}
delete r.exact;
}
if (r.ideal !== undefined) {
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) {
var iceServers = [];
// Use .url for FireFox.
for (var i = 0; i < urls.length; i++) {
var iceServer =
window.createIceServer(urls[i], username, password);
if (iceServer !== null) {
iceServers.push(iceServer);
}
}
return iceServers;
navigator.getUserMedia = getUserMedia;
// Shim for mediaDevices on older versions.
if (!navigator.mediaDevices) {
navigator.mediaDevices = {getUserMedia: requestUserMedia,
addEventListener: function() { },
removeEventListener: function() { }
};
}
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.
attachMediaStream = function(element, stream) {
console.log('Attaching media stream');
element.mozSrcObject = stream;
};
reattachMediaStream = function(to, from) {
console.log('Reattaching media stream');
to.mozSrcObject = from.mozSrcObject;
};
} else if (navigator.webkitGetUserMedia) {
console.log('This appears to be Chrome');
webrtcUtils.log('This appears to be Chrome');
webrtcDetectedBrowser = 'chrome';
// Temporary fix until crbug/374263 is fixed.
// Setting Chrome version to 999, if version is unavailable.
var result = navigator.userAgent.match(/Chrom(e|ium)\/([0-9]+)\./);
if (result !== null) {
webrtcDetectedVersion = parseInt(result[2], 10);
} else {
webrtcDetectedVersion = 999;
}
// Creates iceServer from the url for Chrome M33 and earlier.
window.createIceServer = function(url, username, password) {
var iceServer = null;
var urlParts = url.split(':');
if (urlParts[0].indexOf('stun') === 0) {
// Create iceServer with stun url.
iceServer = {
'url': url
// the detected chrome version.
webrtcDetectedVersion =
parseInt(navigator.userAgent.match(/Chrom(e|ium)\/([0-9]+)\./)[2], 10);
// the minimum chrome version still supported by adapter.
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);
}
var fixChromeStats = function(response) {
var standardReport = {};
var reports = response.result();
reports.forEach(function(report) {
var standardStats = {
id: report.id,
timestamp: report.timestamp,
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.
iceServer = {
'url': url,
'credential': password,
'username': username
if (arguments.length >= 2) {
var successCallbackWrapper = function(response) {
args[1](fixChromeStats(response));
};
return origGetStats.apply(this, [successCallbackWrapper, arguments[0]]);
}
// promise-support
return new Promise(function(resolve, reject) {
origGetStats.apply(self, [resolve, reject]);
});
};
return pc;
};
// add promise support
['createOffer', 'createAnswer'].forEach(function(method) {
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 iceServer;
return cc;
};
// Creates an ICEServer object from multiple URLs.
window.createIceServers = function(urls, username, password) {
return [{
'urls': urls,
'credential': password,
'username': username
}];
getUserMedia = function(constraints, onSuccess, onError) {
if (constraints.audio) {
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;
// The RTCPeerConnection object.
RTCPeerConnection = function(pcConfig, pcConstraints) {
return new webkitRTCPeerConnection(pcConfig, pcConstraints);
};
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: ''};
}));
});
});
}};
}
// Get UserMedia (only difference is the prefix).
// Code from Adam Barth.
getUserMedia = navigator.webkitGetUserMedia.bind(navigator);
navigator.getUserMedia = getUserMedia;
// 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.
attachMediaStream = function(element, stream) {
if (typeof element.srcObject !== 'undefined') {
element.srcObject = stream;
} else if (typeof element.mozSrcObject !== 'undefined') {
element.mozSrcObject = stream;
} else if (typeof element.src !== 'undefined') {
element.src = URL.createObjectURL(stream);
} else {
console.log('Error attaching stream to element.');
webrtcUtils.log('Error attaching stream to element.');
}
};
reattachMediaStream = function(to, from) {
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 {
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 @@ -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;
};
// Adapter to support navigator.mediaDevices API.

27
static/js/services/constraints.js

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

4
static/js/services/mediastream.js

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

Loading…
Cancel
Save