Socket
Socket
Sign inDemoInstall

webrtc-adapter

Package Overview
Dependencies
Maintainers
4
Versions
120
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

webrtc-adapter - npm Package Compare versions

Comparing version 1.0.7 to 1.1.0

src/js/safari/safari_shim.js

23

Gruntfile.js

@@ -63,27 +63,20 @@ 'use strict';

all: {
'pre-commit': 'jshint jscs'
'pre-commit': 'lint'
}
},
jshint: {
eslint: {
options: {
jshintrc: '.jshintrc'
configFile: '.eslintrc'
},
files: ['adapter_core.js', 'test/*.js']
target: ['src/**/*.js', 'test/*.js']
},
jscs: {
src: ['adapter_core.js', 'test/*.js'],
options: {
config: '.jscsrc',
'excludeFiles': [
]
}
}
});
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.loadNpmTasks('grunt-jscs');
grunt.loadNpmTasks('grunt-githooks');
grunt.loadNpmTasks('grunt-eslint');
grunt.loadNpmTasks('grunt-browserify');
grunt.registerTask('default', ['jshint', 'jscs', 'browserify']);
grunt.registerTask('default', ['eslint', 'browserify']);
grunt.registerTask('lint', ['eslint']);
grunt.registerTask('build', ['browserify']);
};

@@ -11,2 +11,4 @@ (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){

*/
/* eslint-env node */
'use strict';

@@ -24,7 +26,7 @@

// Uncomment if you do not want any logging at all including the switch
// statement below. Can also be turned off in the browser via
// adapter.disableLog(true) but then logging from the switch statement below
// will still appear.
//require('./utils').disableLog(true);
// Comment out the line below if you want logging to occur, including logging
// for the switch statement below. Can also be turned on in the browser via
// adapter.disableLog(false), but then logging from the switch statement below
// will not appear.
require('./utils').disableLog(true);

@@ -35,5 +37,7 @@ // Browser shims.

var firefoxShim = require('./firefox/firefox_shim') || null;
var safariShim = require('./safari/safari_shim') || null;
// Shim browser if found.
switch (browserDetails.browser) {
case 'opera': // fallthrough as it uses chrome shims
case 'chrome':

@@ -44,3 +48,3 @@ if (!chromeShim || !chromeShim.shimPeerConnection) {

}
logging('adapter.js shimming chrome!');
logging('adapter.js shimming chrome.');
// Export to the adapter global object visible in the browser.

@@ -54,2 +58,16 @@ module.exports.browserShim = chromeShim;

break;
case 'firefox':
if (!firefoxShim || !firefoxShim.shimPeerConnection) {
logging('Firefox shim is not included in this adapter release.');
return;
}
logging('adapter.js shimming firefox.');
// Export to the adapter global object visible in the browser.
module.exports.browserShim = firefoxShim;
firefoxShim.shimGetUserMedia();
firefoxShim.shimSourceObject();
firefoxShim.shimPeerConnection();
firefoxShim.shimOnTrack();
break;
case 'edge':

@@ -60,3 +78,3 @@ if (!edgeShim || !edgeShim.shimPeerConnection) {

}
logging('adapter.js shimming edge!');
logging('adapter.js shimming edge.');
// Export to the adapter global object visible in the browser.

@@ -67,15 +85,12 @@ module.exports.browserShim = edgeShim;

break;
case 'firefox':
if (!firefoxShim || !firefoxShim.shimPeerConnection) {
logging('Firefox shim is not included in this adapter release.');
case 'safari':
if (!safariShim) {
logging('Safari shim is not included in this adapter release.');
return;
}
logging('adapter.js shimming firefox!');
logging('adapter.js shimming safari.');
// Export to the adapter global object visible in the browser.
module.exports.browserShim = firefoxShim;
module.exports.browserShim = safariShim;
firefoxShim.shimGetUserMedia();
firefoxShim.shimSourceObject();
firefoxShim.shimPeerConnection();
firefoxShim.shimOnTrack();
safariShim.shimGetUserMedia();
break;

@@ -87,3 +102,3 @@ default:

},{"./chrome/chrome_shim":3,"./edge/edge_shim":1,"./firefox/firefox_shim":5,"./utils":7}],3:[function(require,module,exports){
},{"./chrome/chrome_shim":3,"./edge/edge_shim":1,"./firefox/firefox_shim":5,"./safari/safari_shim":7,"./utils":8}],3:[function(require,module,exports){
/*

@@ -96,2 +111,3 @@ * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.

*/
/* eslint-env node */
'use strict';

@@ -106,3 +122,5 @@ var logging = require('../utils.js').log;

Object.defineProperty(window.RTCPeerConnection.prototype, 'ontrack', {
get: function() { return this._ontrack; },
get: function() {
return this._ontrack;
},
set: function(f) {

@@ -116,4 +134,4 @@ var self = this;

this.addEventListener('addstream', this._ontrackpoly = function(e) {
// onaddstream does not fire when a track is added to an existing stream.
// but stream.onaddtrack is implemented so we use that
// onaddstream does not fire when a track is added to an existing
// stream. But stream.onaddtrack is implemented so we use that.
e.stream.addEventListener('addtrack', function(te) {

@@ -161,4 +179,4 @@ var event = new Event('track');

this.src = URL.createObjectURL(stream);
// We need to recreate the blob url when a track is added or removed.
// Doing it manually since we want to avoid a recursion.
// We need to recreate the blob url when a track is added or
// removed. Doing it manually since we want to avoid a recursion.
stream.addEventListener('addtrack', function() {

@@ -192,5 +210,5 @@ if (self.src) {

var pc = new webkitRTCPeerConnection(pcConfig, pcConstraints); // jscs:ignore requireCapitalizedConstructors
var pc = new webkitRTCPeerConnection(pcConfig, pcConstraints);
var origGetStats = pc.getStats.bind(pc);
pc.getStats = function(selector, successCallback, errorCallback) { // jshint ignore: line
pc.getStats = function(selector, successCallback, errorCallback) {
var self = this;

@@ -235,4 +253,4 @@ var args = arguments;

if (args.length === 1 && typeof selector === 'object') {
origGetStats.apply(self, [
function(response) {
origGetStats.apply(self,
[function(response) {
resolve.apply(null, [fixChromeStats_(response)]);

@@ -254,8 +272,3 @@ }, reject]);

get: function() {
if (arguments.length) {
return webkitRTCPeerConnection.generateCertificate.apply(null,
arguments);
} else {
return webkitRTCPeerConnection.generateCertificate;
}
return webkitRTCPeerConnection.generateCertificate;
}

@@ -276,32 +289,33 @@ });

});
} else {
return nativeMethod.apply(this, arguments);
}
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]);
}
}]
);
['setLocalDescription', 'setRemoteDescription', 'addIceCandidate']
.forEach(function(method) {
var nativeMethod = webkitRTCPeerConnection.prototype[method];
webkitRTCPeerConnection.prototype[method] = function() {
var args = arguments;
var self = this;
args[0] = new ((method === 'addIceCandidate')?
RTCIceCandidate : RTCSessionDescription)(args[0]);
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]);
}
}]
);
});
};
});
};
});
},

@@ -342,3 +356,3 @@

},{"../utils.js":7,"./getusermedia":4}],4:[function(require,module,exports){
},{"../utils.js":8,"./getusermedia":4}],4:[function(require,module,exports){
/*

@@ -351,2 +365,3 @@ * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.

*/
/* eslint-env node */
'use strict';

@@ -409,2 +424,3 @@ var logging = require('../utils.js').log;

var getUserMedia_ = function(constraints, onSuccess, onError) {
constraints = JSON.parse(JSON.stringify(constraints));
if (constraints.audio) {

@@ -429,16 +445,18 @@ constraints.audio = constraintsToChrome_(constraints.audio);

if (!navigator.mediaDevices) {
navigator.mediaDevices = {getUserMedia: getUserMediaPromise_,
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: ''};
}));
navigator.mediaDevices = {
getUserMedia: getUserMediaPromise_,
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: ''};
}));
});
});
});
}};
}
};
}

@@ -483,3 +501,3 @@

},{"../utils.js":7}],5:[function(require,module,exports){
},{"../utils.js":8}],5:[function(require,module,exports){
/*

@@ -492,2 +510,3 @@ * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.

*/
/* eslint-env node */
'use strict';

@@ -503,3 +522,5 @@

Object.defineProperty(window.RTCPeerConnection.prototype, 'ontrack', {
get: function() { return this._ontrack; },
get: function() {
return this._ontrack;
},
set: function(f) {

@@ -572,3 +593,3 @@ if (this._ontrack) {

}
return new mozRTCPeerConnection(pcConfig, pcConstraints); // jscs:ignore requireCapitalizedConstructors
return new mozRTCPeerConnection(pcConfig, pcConstraints);
};

@@ -581,8 +602,3 @@ window.RTCPeerConnection.prototype = mozRTCPeerConnection.prototype;

get: function() {
if (arguments.length) {
return mozRTCPeerConnection.generateCertificate.apply(null,
arguments);
} else {
return mozRTCPeerConnection.generateCertificate;
}
return mozRTCPeerConnection.generateCertificate;
}

@@ -595,2 +611,13 @@ });

}
// shim away need for obsolete RTCIceCandidate/RTCSessionDescription.
['setLocalDescription', 'setRemoteDescription', 'addIceCandidate']
.forEach(function(method) {
var nativeMethod = RTCPeerConnection.prototype[method];
RTCPeerConnection.prototype[method] = function() {
arguments[0] = new ((method === 'addIceCandidate')?
RTCIceCandidate : RTCSessionDescription)(arguments[0]);
return nativeMethod.apply(this, arguments);
};
});
},

@@ -645,2 +672,3 @@

};
constraints = JSON.parse(JSON.stringify(constraints));
if (browserDetails.version < 38) {

@@ -677,10 +705,10 @@ logging('spec: ' + JSON.stringify(constraints));

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);
});
};
return new Promise(function(resolve) {
var infos = [
{kind: 'audioinput', deviceId: 'default', label: '', groupId: ''},
{kind: 'videoinput', deviceId: 'default', label: '', groupId: ''}
];
resolve(infos);
});
};

@@ -724,3 +752,3 @@ if (browserDetails.version < 41) {

},{"../utils":7,"./getusermedia":6}],6:[function(require,module,exports){
},{"../utils":8,"./getusermedia":6}],6:[function(require,module,exports){
/*

@@ -733,2 +761,3 @@ * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.

*/
/* eslint-env node */
'use strict';

@@ -786,2 +815,3 @@

};
constraints = JSON.parse(JSON.stringify(constraints));
if (browserDetails.version < 38) {

@@ -818,10 +848,10 @@ logging('spec: ' + JSON.stringify(constraints));

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);
});
};
return new Promise(function(resolve) {
var infos = [
{kind: 'audioinput', deviceId: 'default', label: '', groupId: ''},
{kind: 'videoinput', deviceId: 'default', label: '', groupId: ''}
];
resolve(infos);
});
};

@@ -843,3 +873,3 @@ if (browserDetails.version < 41) {

},{"../utils":7}],7:[function(require,module,exports){
},{"../utils":8}],7:[function(require,module,exports){
/*

@@ -853,3 +883,40 @@ * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.

'use strict';
var safariShim = {
// TODO: DrAlex, should be here, double check against LayoutTests
// shimOnTrack: function() { },
// TODO: DrAlex
// attachMediaStream: function(element, stream) { },
// reattachMediaStream: function(to, from) { },
// TODO: once the back-end for the mac port is done, add.
// TODO: check for webkitGTK+
// shimPeerConnection: function() { },
shimGetUserMedia: function() {
navigator.getUserMedia = navigator.webkitGetUserMedia;
}
};
// Expose public methods.
module.exports = {
shimGetUserMedia: safariShim.shimGetUserMedia
// TODO
// shimOnTrack: safariShim.shimOnTrack,
// shimPeerConnection: safariShim.shimPeerConnection,
// attachMediaStream: safariShim.attachMediaStream,
// reattachMediaStream: safariShim.reattachMediaStream
};
},{}],8:[function(require,module,exports){
/*
* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree.
*/
/* eslint-env node */
'use strict';
var logDisabled_ = false;

@@ -874,3 +941,5 @@

}
console.log.apply(console, arguments);
if (typeof console !== 'undefined' && typeof console.log === 'function') {
console.log.apply(console, arguments);
}
}

@@ -905,7 +974,10 @@ },

// Fail early if it's not a browser
if (typeof window === 'undefined' || !window.navigator) {
result.browser = 'Not a browser.';
return result;
} else if (navigator.mozGetUserMedia) {
// Firefox.
}
// Firefox.
if (navigator.mozGetUserMedia) {
result.browser = 'firefox';

@@ -915,11 +987,43 @@ result.version = this.extractVersion(navigator.userAgent,

result.minVersion = 31;
} else if (navigator.webkitGetUserMedia && window.webkitRTCPeerConnection) {
// Chrome, Chromium, WebView, Opera and other WebKit browsers.
result.browser = 'chrome';
result.version = this.extractVersion(navigator.userAgent,
// all webkit-based browsers
} else if (navigator.webkitGetUserMedia) {
// Chrome, Chromium, Webview, Opera, all use the chrome shim for now
if (window.webkitRTCPeerConnection) {
result.browser = 'chrome';
result.version = this.extractVersion(navigator.userAgent,
/Chrom(e|ium)\/([0-9]+)\./, 2);
result.minVersion = 38;
} else if(navigator.mediaDevices &&
result.minVersion = 38;
// Safari or unknown webkit-based
// for the time being Safari has support for MediaStreams but not webRTC
} else {
// Safari UA substrings of interest for reference:
// - webkit version: AppleWebKit/602.1.25 (also used in Op,Cr)
// - safari UI version: Version/9.0.3 (unique to Safari)
// - safari UI webkit version: Safari/601.4.4 (also used in Op,Cr)
//
// if the webkit version and safari UI webkit versions are equals,
// ... this is a stable version.
//
// only the internal webkit version is important today to know if
// media streams are supported
//
if (navigator.userAgent.match(/Version\/(\d+).(\d+)/)) {
result.browser = 'safari';
result.version = this.extractVersion(navigator.userAgent,
/AppleWebKit\/([0-9]+)\./, 1);
result.minVersion = 602;
// unknown webkit-based browser
} else {
result.browser = 'Unsupported webkit-based browser ' +
'with GUM support but no WebRTC support.';
return result;
}
}
// Edge.
} else if (navigator.mediaDevices &&
navigator.userAgent.match(/Edge\/(\d+).(\d+)$/)) {
// Edge.
result.browser = 'edge';

@@ -929,2 +1033,4 @@ result.version = this.extractVersion(navigator.userAgent,

result.minVersion = 10547;
// Default fallthrough: not supported.
} else {

@@ -931,0 +1037,0 @@ result.browser = 'Not a supported browser.';

@@ -11,2 +11,4 @@ (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){

*/
/* eslint-env node */
'use strict';

@@ -24,7 +26,7 @@

// Uncomment if you do not want any logging at all including the switch
// statement below. Can also be turned off in the browser via
// adapter.disableLog(true) but then logging from the switch statement below
// will still appear.
//require('./utils').disableLog(true);
// Comment out the line below if you want logging to occur, including logging
// for the switch statement below. Can also be turned on in the browser via
// adapter.disableLog(false), but then logging from the switch statement below
// will not appear.
require('./utils').disableLog(true);

@@ -35,5 +37,7 @@ // Browser shims.

var firefoxShim = require('./firefox/firefox_shim') || null;
var safariShim = require('./safari/safari_shim') || null;
// Shim browser if found.
switch (browserDetails.browser) {
case 'opera': // fallthrough as it uses chrome shims
case 'chrome':

@@ -44,3 +48,3 @@ if (!chromeShim || !chromeShim.shimPeerConnection) {

}
logging('adapter.js shimming chrome!');
logging('adapter.js shimming chrome.');
// Export to the adapter global object visible in the browser.

@@ -54,2 +58,16 @@ module.exports.browserShim = chromeShim;

break;
case 'firefox':
if (!firefoxShim || !firefoxShim.shimPeerConnection) {
logging('Firefox shim is not included in this adapter release.');
return;
}
logging('adapter.js shimming firefox.');
// Export to the adapter global object visible in the browser.
module.exports.browserShim = firefoxShim;
firefoxShim.shimGetUserMedia();
firefoxShim.shimSourceObject();
firefoxShim.shimPeerConnection();
firefoxShim.shimOnTrack();
break;
case 'edge':

@@ -60,3 +78,3 @@ if (!edgeShim || !edgeShim.shimPeerConnection) {

}
logging('adapter.js shimming edge!');
logging('adapter.js shimming edge.');
// Export to the adapter global object visible in the browser.

@@ -67,15 +85,12 @@ module.exports.browserShim = edgeShim;

break;
case 'firefox':
if (!firefoxShim || !firefoxShim.shimPeerConnection) {
logging('Firefox shim is not included in this adapter release.');
case 'safari':
if (!safariShim) {
logging('Safari shim is not included in this adapter release.');
return;
}
logging('adapter.js shimming firefox!');
logging('adapter.js shimming safari.');
// Export to the adapter global object visible in the browser.
module.exports.browserShim = firefoxShim;
module.exports.browserShim = safariShim;
firefoxShim.shimGetUserMedia();
firefoxShim.shimSourceObject();
firefoxShim.shimPeerConnection();
firefoxShim.shimOnTrack();
safariShim.shimGetUserMedia();
break;

@@ -87,3 +102,3 @@ default:

},{"./chrome/chrome_shim":3,"./edge/edge_shim":1,"./firefox/firefox_shim":5,"./utils":7}],3:[function(require,module,exports){
},{"./chrome/chrome_shim":3,"./edge/edge_shim":1,"./firefox/firefox_shim":5,"./safari/safari_shim":7,"./utils":8}],3:[function(require,module,exports){
/*

@@ -96,2 +111,3 @@ * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.

*/
/* eslint-env node */
'use strict';

@@ -106,3 +122,5 @@ var logging = require('../utils.js').log;

Object.defineProperty(window.RTCPeerConnection.prototype, 'ontrack', {
get: function() { return this._ontrack; },
get: function() {
return this._ontrack;
},
set: function(f) {

@@ -116,4 +134,4 @@ var self = this;

this.addEventListener('addstream', this._ontrackpoly = function(e) {
// onaddstream does not fire when a track is added to an existing stream.
// but stream.onaddtrack is implemented so we use that
// onaddstream does not fire when a track is added to an existing
// stream. But stream.onaddtrack is implemented so we use that.
e.stream.addEventListener('addtrack', function(te) {

@@ -161,4 +179,4 @@ var event = new Event('track');

this.src = URL.createObjectURL(stream);
// We need to recreate the blob url when a track is added or removed.
// Doing it manually since we want to avoid a recursion.
// We need to recreate the blob url when a track is added or
// removed. Doing it manually since we want to avoid a recursion.
stream.addEventListener('addtrack', function() {

@@ -192,5 +210,5 @@ if (self.src) {

var pc = new webkitRTCPeerConnection(pcConfig, pcConstraints); // jscs:ignore requireCapitalizedConstructors
var pc = new webkitRTCPeerConnection(pcConfig, pcConstraints);
var origGetStats = pc.getStats.bind(pc);
pc.getStats = function(selector, successCallback, errorCallback) { // jshint ignore: line
pc.getStats = function(selector, successCallback, errorCallback) {
var self = this;

@@ -235,4 +253,4 @@ var args = arguments;

if (args.length === 1 && typeof selector === 'object') {
origGetStats.apply(self, [
function(response) {
origGetStats.apply(self,
[function(response) {
resolve.apply(null, [fixChromeStats_(response)]);

@@ -254,8 +272,3 @@ }, reject]);

get: function() {
if (arguments.length) {
return webkitRTCPeerConnection.generateCertificate.apply(null,
arguments);
} else {
return webkitRTCPeerConnection.generateCertificate;
}
return webkitRTCPeerConnection.generateCertificate;
}

@@ -276,32 +289,33 @@ });

});
} else {
return nativeMethod.apply(this, arguments);
}
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]);
}
}]
);
['setLocalDescription', 'setRemoteDescription', 'addIceCandidate']
.forEach(function(method) {
var nativeMethod = webkitRTCPeerConnection.prototype[method];
webkitRTCPeerConnection.prototype[method] = function() {
var args = arguments;
var self = this;
args[0] = new ((method === 'addIceCandidate')?
RTCIceCandidate : RTCSessionDescription)(args[0]);
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]);
}
}]
);
});
};
});
};
});
},

@@ -342,3 +356,3 @@

},{"../utils.js":7,"./getusermedia":4}],4:[function(require,module,exports){
},{"../utils.js":8,"./getusermedia":4}],4:[function(require,module,exports){
/*

@@ -351,2 +365,3 @@ * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.

*/
/* eslint-env node */
'use strict';

@@ -409,2 +424,3 @@ var logging = require('../utils.js').log;

var getUserMedia_ = function(constraints, onSuccess, onError) {
constraints = JSON.parse(JSON.stringify(constraints));
if (constraints.audio) {

@@ -429,16 +445,18 @@ constraints.audio = constraintsToChrome_(constraints.audio);

if (!navigator.mediaDevices) {
navigator.mediaDevices = {getUserMedia: getUserMediaPromise_,
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: ''};
}));
navigator.mediaDevices = {
getUserMedia: getUserMediaPromise_,
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: ''};
}));
});
});
});
}};
}
};
}

@@ -483,3 +501,3 @@

},{"../utils.js":7}],5:[function(require,module,exports){
},{"../utils.js":8}],5:[function(require,module,exports){
/*

@@ -492,2 +510,3 @@ * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.

*/
/* eslint-env node */
'use strict';

@@ -503,3 +522,5 @@

Object.defineProperty(window.RTCPeerConnection.prototype, 'ontrack', {
get: function() { return this._ontrack; },
get: function() {
return this._ontrack;
},
set: function(f) {

@@ -572,3 +593,3 @@ if (this._ontrack) {

}
return new mozRTCPeerConnection(pcConfig, pcConstraints); // jscs:ignore requireCapitalizedConstructors
return new mozRTCPeerConnection(pcConfig, pcConstraints);
};

@@ -581,8 +602,3 @@ window.RTCPeerConnection.prototype = mozRTCPeerConnection.prototype;

get: function() {
if (arguments.length) {
return mozRTCPeerConnection.generateCertificate.apply(null,
arguments);
} else {
return mozRTCPeerConnection.generateCertificate;
}
return mozRTCPeerConnection.generateCertificate;
}

@@ -595,2 +611,13 @@ });

}
// shim away need for obsolete RTCIceCandidate/RTCSessionDescription.
['setLocalDescription', 'setRemoteDescription', 'addIceCandidate']
.forEach(function(method) {
var nativeMethod = RTCPeerConnection.prototype[method];
RTCPeerConnection.prototype[method] = function() {
arguments[0] = new ((method === 'addIceCandidate')?
RTCIceCandidate : RTCSessionDescription)(arguments[0]);
return nativeMethod.apply(this, arguments);
};
});
},

@@ -645,2 +672,3 @@

};
constraints = JSON.parse(JSON.stringify(constraints));
if (browserDetails.version < 38) {

@@ -677,10 +705,10 @@ logging('spec: ' + JSON.stringify(constraints));

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);
});
};
return new Promise(function(resolve) {
var infos = [
{kind: 'audioinput', deviceId: 'default', label: '', groupId: ''},
{kind: 'videoinput', deviceId: 'default', label: '', groupId: ''}
];
resolve(infos);
});
};

@@ -724,3 +752,3 @@ if (browserDetails.version < 41) {

},{"../utils":7,"./getusermedia":6}],6:[function(require,module,exports){
},{"../utils":8,"./getusermedia":6}],6:[function(require,module,exports){
/*

@@ -733,2 +761,3 @@ * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.

*/
/* eslint-env node */
'use strict';

@@ -786,2 +815,3 @@

};
constraints = JSON.parse(JSON.stringify(constraints));
if (browserDetails.version < 38) {

@@ -818,10 +848,10 @@ logging('spec: ' + JSON.stringify(constraints));

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);
});
};
return new Promise(function(resolve) {
var infos = [
{kind: 'audioinput', deviceId: 'default', label: '', groupId: ''},
{kind: 'videoinput', deviceId: 'default', label: '', groupId: ''}
];
resolve(infos);
});
};

@@ -843,3 +873,3 @@ if (browserDetails.version < 41) {

},{"../utils":7}],7:[function(require,module,exports){
},{"../utils":8}],7:[function(require,module,exports){
/*

@@ -853,3 +883,40 @@ * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.

'use strict';
var safariShim = {
// TODO: DrAlex, should be here, double check against LayoutTests
// shimOnTrack: function() { },
// TODO: DrAlex
// attachMediaStream: function(element, stream) { },
// reattachMediaStream: function(to, from) { },
// TODO: once the back-end for the mac port is done, add.
// TODO: check for webkitGTK+
// shimPeerConnection: function() { },
shimGetUserMedia: function() {
navigator.getUserMedia = navigator.webkitGetUserMedia;
}
};
// Expose public methods.
module.exports = {
shimGetUserMedia: safariShim.shimGetUserMedia
// TODO
// shimOnTrack: safariShim.shimOnTrack,
// shimPeerConnection: safariShim.shimPeerConnection,
// attachMediaStream: safariShim.attachMediaStream,
// reattachMediaStream: safariShim.reattachMediaStream
};
},{}],8:[function(require,module,exports){
/*
* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree.
*/
/* eslint-env node */
'use strict';
var logDisabled_ = false;

@@ -874,3 +941,5 @@

}
console.log.apply(console, arguments);
if (typeof console !== 'undefined' && typeof console.log === 'function') {
console.log.apply(console, arguments);
}
}

@@ -905,7 +974,10 @@ },

// Fail early if it's not a browser
if (typeof window === 'undefined' || !window.navigator) {
result.browser = 'Not a browser.';
return result;
} else if (navigator.mozGetUserMedia) {
// Firefox.
}
// Firefox.
if (navigator.mozGetUserMedia) {
result.browser = 'firefox';

@@ -915,11 +987,43 @@ result.version = this.extractVersion(navigator.userAgent,

result.minVersion = 31;
} else if (navigator.webkitGetUserMedia && window.webkitRTCPeerConnection) {
// Chrome, Chromium, WebView, Opera and other WebKit browsers.
result.browser = 'chrome';
result.version = this.extractVersion(navigator.userAgent,
// all webkit-based browsers
} else if (navigator.webkitGetUserMedia) {
// Chrome, Chromium, Webview, Opera, all use the chrome shim for now
if (window.webkitRTCPeerConnection) {
result.browser = 'chrome';
result.version = this.extractVersion(navigator.userAgent,
/Chrom(e|ium)\/([0-9]+)\./, 2);
result.minVersion = 38;
} else if(navigator.mediaDevices &&
result.minVersion = 38;
// Safari or unknown webkit-based
// for the time being Safari has support for MediaStreams but not webRTC
} else {
// Safari UA substrings of interest for reference:
// - webkit version: AppleWebKit/602.1.25 (also used in Op,Cr)
// - safari UI version: Version/9.0.3 (unique to Safari)
// - safari UI webkit version: Safari/601.4.4 (also used in Op,Cr)
//
// if the webkit version and safari UI webkit versions are equals,
// ... this is a stable version.
//
// only the internal webkit version is important today to know if
// media streams are supported
//
if (navigator.userAgent.match(/Version\/(\d+).(\d+)/)) {
result.browser = 'safari';
result.version = this.extractVersion(navigator.userAgent,
/AppleWebKit\/([0-9]+)\./, 1);
result.minVersion = 602;
// unknown webkit-based browser
} else {
result.browser = 'Unsupported webkit-based browser ' +
'with GUM support but no WebRTC support.';
return result;
}
}
// Edge.
} else if (navigator.mediaDevices &&
navigator.userAgent.match(/Edge\/(\d+).(\d+)$/)) {
// Edge.
result.browser = 'edge';

@@ -929,2 +1033,4 @@ result.version = this.extractVersion(navigator.userAgent,

result.minVersion = 10547;
// Default fallthrough: not supported.
} else {

@@ -931,0 +1037,0 @@ result.browser = 'Not a supported browser.';

{
"name": "webrtc-adapter",
"version": "1.0.7",
"version": "1.1.0",
"description": "A shim to insulate apps from WebRTC spec changes and browser prefix differences",

@@ -15,7 +15,7 @@ "license": "BSD-3-Clause",

"scripts": {
"preversion": "git stash && git checkout master && git pull && grunt && test/run-tests | faucet && git checkout -B bumpVersion",
"preversion": "git stash && git checkout master && git pull && npm test | faucet && git checkout -B bumpVersion",
"version": "grunt build",
"postversion": "git push --force --set-upstream origin bumpVersion --follow-tags && export GITTAG=\"echo $(git describe --abbrev=0 --tags | sed 's/^v//')\" && git checkout gh-pages && git pull && cp out/adapter.js . && cp adapter.js adapter-`$GITTAG`.js && rm adapter-latest.js && ln -s adapter-`$GITTAG`.js adapter-latest.js && mkdir adapter-`$GITTAG`-variants && cp out/adapter_*.js adapter-`$GITTAG`-variants/ && git add adapter.js adapter-latest.js adapter-`$GITTAG`.js adapter-`$GITTAG`-variants && git commit -m `$GITTAG` && git push --set-upstream origin gh-pages && npm publish",
"postversion": "git push --force --set-upstream origin bumpVersion --follow-tags && export GITTAG=\"echo $(git describe --abbrev=0 --tags | sed 's/^v//')\" && git checkout gh-pages && git pull && cp out/adapter.js . && cp adapter.js adapter-`$GITTAG`.js && rm adapter-latest.js && ln -s adapter-`$GITTAG`.js adapter-latest.js && mkdir adapter-`$GITTAG`-variants && cp out/adapter_*.js adapter-`$GITTAG`-variants/ && git add adapter.js adapter-latest.js adapter-`$GITTAG`.js adapter-`$GITTAG`-variants && git commit -m `$GITTAG` && git push --set-upstream origin gh-pages && git checkout master",
"prepublish": "grunt build",
"test": "grunt && test/run-tests | faucet"
"test": "grunt && node test/run-tests.js"
},

@@ -28,6 +28,5 @@ "dependencies": {},

"grunt-cli": ">=0.1.9",
"grunt-contrib-jshint": "^0.11.2",
"grunt-eslint": "^17.2.0",
"grunt-githooks": "^0.3.1",
"grunt-jscs": "^2.0.0",
"selenium-webdriver": "^2.48.0",
"selenium-webdriver": "^2.52.0",
"tape": "^4.0.0",

@@ -34,0 +33,0 @@ "travis-multirunner": "^3.0.0",

@@ -53,5 +53,8 @@ [![Build Status](https://travis-ci.org/webrtc/adapter.svg)](https://travis-ci.org/webrtc/adapter)

* Make sure your repository is clean, i.e. no untracked files etc
* Run `npm version patch -m 'bump to %s'` and type in your password lots of times (setting up credential caching is probably a good idea).
* Depending on the impact of the release, either use `patch`, `minor` or `major` in place of `<version>`. Run `npm version <version> -m 'bump to %s'` and type in your password lots of times (setting up credential caching is probably a good idea).
* Create and merge the PR if green in the GitHub web ui
* Run `git pull`
* Run `npm publish`
* Done! There should now be a new release published to NPM and the gh-pages branch.
Note: Currently only tested on Linux, not sure about Mac but will definitely not work on Windows.

@@ -8,2 +8,4 @@ /*

*/
/* eslint-env node */
'use strict';

@@ -21,7 +23,7 @@

// Uncomment if you do not want any logging at all including the switch
// statement below. Can also be turned off in the browser via
// adapter.disableLog(true) but then logging from the switch statement below
// will still appear.
//require('./utils').disableLog(true);
// Comment out the line below if you want logging to occur, including logging
// for the switch statement below. Can also be turned on in the browser via
// adapter.disableLog(false), but then logging from the switch statement below
// will not appear.
require('./utils').disableLog(true);

@@ -32,5 +34,7 @@ // Browser shims.

var firefoxShim = require('./firefox/firefox_shim') || null;
var safariShim = require('./safari/safari_shim') || null;
// Shim browser if found.
switch (browserDetails.browser) {
case 'opera': // fallthrough as it uses chrome shims
case 'chrome':

@@ -41,3 +45,3 @@ if (!chromeShim || !chromeShim.shimPeerConnection) {

}
logging('adapter.js shimming chrome!');
logging('adapter.js shimming chrome.');
// Export to the adapter global object visible in the browser.

@@ -51,2 +55,16 @@ module.exports.browserShim = chromeShim;

break;
case 'firefox':
if (!firefoxShim || !firefoxShim.shimPeerConnection) {
logging('Firefox shim is not included in this adapter release.');
return;
}
logging('adapter.js shimming firefox.');
// Export to the adapter global object visible in the browser.
module.exports.browserShim = firefoxShim;
firefoxShim.shimGetUserMedia();
firefoxShim.shimSourceObject();
firefoxShim.shimPeerConnection();
firefoxShim.shimOnTrack();
break;
case 'edge':

@@ -57,3 +75,3 @@ if (!edgeShim || !edgeShim.shimPeerConnection) {

}
logging('adapter.js shimming edge!');
logging('adapter.js shimming edge.');
// Export to the adapter global object visible in the browser.

@@ -64,15 +82,12 @@ module.exports.browserShim = edgeShim;

break;
case 'firefox':
if (!firefoxShim || !firefoxShim.shimPeerConnection) {
logging('Firefox shim is not included in this adapter release.');
case 'safari':
if (!safariShim) {
logging('Safari shim is not included in this adapter release.');
return;
}
logging('adapter.js shimming firefox!');
logging('adapter.js shimming safari.');
// Export to the adapter global object visible in the browser.
module.exports.browserShim = firefoxShim;
module.exports.browserShim = safariShim;
firefoxShim.shimGetUserMedia();
firefoxShim.shimSourceObject();
firefoxShim.shimPeerConnection();
firefoxShim.shimOnTrack();
safariShim.shimGetUserMedia();
break;

@@ -79,0 +94,0 @@ default:

@@ -8,2 +8,3 @@ /*

*/
/* eslint-env node */
'use strict';

@@ -18,3 +19,5 @@ var logging = require('../utils.js').log;

Object.defineProperty(window.RTCPeerConnection.prototype, 'ontrack', {
get: function() { return this._ontrack; },
get: function() {
return this._ontrack;
},
set: function(f) {

@@ -28,4 +31,4 @@ var self = this;

this.addEventListener('addstream', this._ontrackpoly = function(e) {
// onaddstream does not fire when a track is added to an existing stream.
// but stream.onaddtrack is implemented so we use that
// onaddstream does not fire when a track is added to an existing
// stream. But stream.onaddtrack is implemented so we use that.
e.stream.addEventListener('addtrack', function(te) {

@@ -73,4 +76,4 @@ var event = new Event('track');

this.src = URL.createObjectURL(stream);
// We need to recreate the blob url when a track is added or removed.
// Doing it manually since we want to avoid a recursion.
// We need to recreate the blob url when a track is added or
// removed. Doing it manually since we want to avoid a recursion.
stream.addEventListener('addtrack', function() {

@@ -104,5 +107,5 @@ if (self.src) {

var pc = new webkitRTCPeerConnection(pcConfig, pcConstraints); // jscs:ignore requireCapitalizedConstructors
var pc = new webkitRTCPeerConnection(pcConfig, pcConstraints);
var origGetStats = pc.getStats.bind(pc);
pc.getStats = function(selector, successCallback, errorCallback) { // jshint ignore: line
pc.getStats = function(selector, successCallback, errorCallback) {
var self = this;

@@ -147,4 +150,4 @@ var args = arguments;

if (args.length === 1 && typeof selector === 'object') {
origGetStats.apply(self, [
function(response) {
origGetStats.apply(self,
[function(response) {
resolve.apply(null, [fixChromeStats_(response)]);

@@ -166,8 +169,3 @@ }, reject]);

get: function() {
if (arguments.length) {
return webkitRTCPeerConnection.generateCertificate.apply(null,
arguments);
} else {
return webkitRTCPeerConnection.generateCertificate;
}
return webkitRTCPeerConnection.generateCertificate;
}

@@ -188,32 +186,33 @@ });

});
} else {
return nativeMethod.apply(this, arguments);
}
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]);
}
}]
);
['setLocalDescription', 'setRemoteDescription', 'addIceCandidate']
.forEach(function(method) {
var nativeMethod = webkitRTCPeerConnection.prototype[method];
webkitRTCPeerConnection.prototype[method] = function() {
var args = arguments;
var self = this;
args[0] = new ((method === 'addIceCandidate')?
RTCIceCandidate : RTCSessionDescription)(args[0]);
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]);
}
}]
);
});
};
});
};
});
},

@@ -220,0 +219,0 @@

@@ -8,2 +8,3 @@ /*

*/
/* eslint-env node */
'use strict';

@@ -66,2 +67,3 @@ var logging = require('../utils.js').log;

var getUserMedia_ = function(constraints, onSuccess, onError) {
constraints = JSON.parse(JSON.stringify(constraints));
if (constraints.audio) {

@@ -86,16 +88,18 @@ constraints.audio = constraintsToChrome_(constraints.audio);

if (!navigator.mediaDevices) {
navigator.mediaDevices = {getUserMedia: getUserMediaPromise_,
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: ''};
}));
navigator.mediaDevices = {
getUserMedia: getUserMediaPromise_,
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: ''};
}));
});
});
});
}};
}
};
}

@@ -102,0 +106,0 @@

@@ -8,2 +8,3 @@ /*

*/
/* eslint-env node */
'use strict';

@@ -31,3 +32,3 @@

SDPUtils.splitSections = function(blob) {
var parts = blob.split('\r\nm=');
var parts = blob.split('\nm=');
return parts.map(function(part, index) {

@@ -46,3 +47,4 @@ return (index > 0 ? 'm=' + part : part).trim() + '\r\n';

// Parses an ICE candidate line. Sample input:
// candidate:702786350 2 udp 41819902 8.8.8.8 60769 typ relay raddr 8.8.8.8 rport 55996"
// candidate:702786350 2 udp 41819902 8.8.8.8 60769 typ relay raddr 8.8.8.8
// rport 55996"
SDPUtils.parseCandidate = function(line) {

@@ -125,7 +127,9 @@ var parts;

parsed.clockRate = parseInt(parts[1], 10); // was: clockrate
parsed.numChannels = parts.length === 3 ? parseInt(parts[2], 10) : 1; // was: channels
// was: channels
parsed.numChannels = parts.length === 3 ? parseInt(parts[2], 10) : 1;
return parsed;
};
// Generate an a=rtpmap line from RTCRtpCodecCapability or RTCRtpCodecParameters.
// Generate an a=rtpmap line from RTCRtpCodecCapability or
// RTCRtpCodecParameters.
SDPUtils.writeRtpMap = function(codec) {

@@ -150,3 +154,4 @@ var pt = codec.payloadType;

// Generates a=extmap line from RTCRtpHeaderExtensionParameters or RTCRtpHeaderExtension.
// Generates a=extmap line from RTCRtpHeaderExtensionParameters or
// RTCRtpHeaderExtension.
SDPUtils.writeExtmap = function(headerExtension) {

@@ -172,3 +177,3 @@ return 'a=extmap:' + (headerExtension.id || headerExtension.preferredId) +

// Generates an a=ftmp line from RTCRtpCodecCapability or RTCRtpCodecParameters.
SDPUtils.writeFtmp = function(codec) {
SDPUtils.writeFmtp = function(codec) {
var line = '';

@@ -179,3 +184,3 @@ var pt = codec.payloadType;

}
if (codec.parameters && codec.parameters.length) {
if (codec.parameters && Object.keys(codec.parameters).length) {
var params = [];

@@ -221,3 +226,3 @@ Object.keys(codec.parameters).forEach(function(param) {

var parts = {
ssrc: line.substr(7, sp - 7)
ssrc: parseInt(line.substr(7, sp - 7), 10)
};

@@ -239,3 +244,4 @@ var colon = line.indexOf(':', sp);

var lines = SDPUtils.splitLines(mediaSection);
lines = lines.concat(SDPUtils.splitLines(sessionpart)); // Search in session part, too.
// Search in session part, too.
lines = lines.concat(SDPUtils.splitLines(sessionpart));
var fpLine = lines.filter(function(line) {

@@ -268,3 +274,4 @@ return line.indexOf('a=fingerprint:') === 0;

var lines = SDPUtils.splitLines(mediaSection);
lines = lines.concat(SDPUtils.splitLines(sessionpart)); // Search in session part, too.
// Search in session part, too.
lines = lines.concat(SDPUtils.splitLines(sessionpart));
var iceParameters = {

@@ -311,3 +318,4 @@ usernameFragment: lines.filter(function(line) {

description.codecs.push(codec);
switch(codec.name.toUpperCase()) {
// parse FEC mechanisms from rtpmap lines.
switch (codec.name.toUpperCase()) {
case 'RED':

@@ -317,2 +325,4 @@ case 'ULPFEC':

break;
default: // only RED and ULPFEC are recognized as FEC mechanisms.
break;
}

@@ -328,3 +338,4 @@ }

// Generates parts of the SDP media section describing the capabilities / parameters.
// Generates parts of the SDP media section describing the capabilities /
// parameters.
SDPUtils.writeRtpDescription = function(kind, caps) {

@@ -350,3 +361,3 @@ var sdp = '';

sdp += SDPUtils.writeRtpMap(codec);
sdp += SDPUtils.writeFtmp(codec);
sdp += SDPUtils.writeFmtp(codec);
sdp += SDPUtils.writeRtcpFb(codec);

@@ -359,2 +370,74 @@ });

// Parses the SDP media section and returns an array of
// RTCRtpEncodingParameters.
SDPUtils.parseRtpEncodingParameters = function(mediaSection) {
var encodingParameters = [];
var description = SDPUtils.parseRtpParameters(mediaSection);
var hasRed = description.fecMechanisms.indexOf('RED') !== -1;
var hasUlpfec = description.fecMechanisms.indexOf('ULPFEC') !== -1;
// filter a=ssrc:... cname:, ignore PlanB-msid
var ssrcs = SDPUtils.matchPrefix(mediaSection, 'a=ssrc:')
.map(function(line) {
return SDPUtils.parseSsrcMedia(line);
})
.filter(function(parts) {
return parts.attribute === 'cname';
});
var primarySsrc = ssrcs.length > 0 && ssrcs[0].ssrc;
var secondarySsrc;
var flows = SDPUtils.matchPrefix(mediaSection, 'a=ssrc-group:FID')
.map(function(line) {
var parts = line.split(' ');
parts.shift();
return parts.map(function(part) {
return parseInt(part, 10);
});
});
if (flows.length > 0 && flows[0].length > 1 && flows[0][0] === primarySsrc) {
secondarySsrc = flows[0][1];
}
description.codecs.forEach(function(codec) {
if (codec.name.toUpperCase() === 'RTX' && codec.parameters.apt) {
var encParam = {
ssrc: primarySsrc,
codecPayloadType: parseInt(codec.parameters.apt, 10),
rtx: {
ssrc: secondarySsrc
}
};
encodingParameters.push(encParam);
if (hasRed) {
encParam = JSON.parse(JSON.stringify(encParam));
encParam.fec = {
ssrc: secondarySsrc,
mechanism: hasUlpfec ? 'red+ulpfec' : 'red'
};
encodingParameters.push(encParam);
}
}
});
if (encodingParameters.length === 0 && primarySsrc) {
encodingParameters.push({
ssrc: primarySsrc
});
}
// we support both b=AS and b=TIAS but interpret AS as TIAS.
var bandwidth = SDPUtils.matchPrefix(mediaSection, 'b=');
if (bandwidth.length) {
if (bandwidth[0].indexOf('b=TIAS:') === 0) {
bandwidth = parseInt(bandwidth[0].substr(7), 10);
} else if (bandwidth[0].indexOf('b=AS:') === 0) {
bandwidth = parseInt(bandwidth[0].substr(5), 10);
}
encodingParameters.forEach(function(params) {
params.maxBitrate = bandwidth;
});
}
return encodingParameters;
};
SDPUtils.writeSessionBoilerplate = function() {

@@ -397,7 +480,8 @@ // FIXME: sess-id should be an NTP timestamp.

sdp += 'a=' + msid;
sdp += 'a=ssrc:' + transceiver.sendSsrc + ' ' + msid;
sdp += 'a=ssrc:' + transceiver.sendEncodingParameters[0].ssrc +
' ' + msid;
}
// FIXME: this should be written by writeRtpDescription.
sdp += 'a=ssrc:' + transceiver.sendSsrc + ' cname:' +
SDPUtils.localCName + '\r\n';
sdp += 'a=ssrc:' + transceiver.sendEncodingParameters[0].ssrc +
' cname:' + SDPUtils.localCName + '\r\n';
return sdp;

@@ -417,2 +501,4 @@ };

return lines[i].substr(2);
default:
// FIXME: What should happen here?
}

@@ -419,0 +505,0 @@ }

@@ -8,2 +8,3 @@ /*

*/
/* eslint-env node */
'use strict';

@@ -38,6 +39,6 @@

var _eventTarget = document.createDocumentFragment();
['addEventListener', 'removeEventListener', 'dispatchEvent'].forEach(
function(method) {
self[method] = _eventTarget[method].bind(_eventTarget);
});
['addEventListener', 'removeEventListener', 'dispatchEvent']
.forEach(function(method) {
self[method] = _eventTarget[method].bind(_eventTarget);
});

@@ -55,4 +56,8 @@ this.onicecandidate = null;

this.remoteStreams = [];
this.getLocalStreams = function() { return self.localStreams; };
this.getRemoteStreams = function() { return self.remoteStreams; };
this.getLocalStreams = function() {
return self.localStreams;
};
this.getRemoteStreams = function() {
return self.remoteStreams;
};

@@ -84,2 +89,5 @@ this.localDescription = new RTCSessionDescription({

throw new TypeError('iceTransportPolicy "none" not supported');
default:
// don't set iceTransportPolicy.
break;
}

@@ -94,5 +102,6 @@ }

server.urls = server.urls.filter(function(url) {
return url.indexOf('transport=udp') !== -1;
return url.indexOf('turn:') === 0 &&
url.indexOf('transport=udp') !== -1;
})[0];
return true;
return !!server.urls;
}

@@ -116,5 +125,6 @@ return false;

var sections = SDPUtils.splitSections(self.localDescription.sdp);
// FIXME: need to apply ice candidates in a way which is async but in-order
// FIXME: need to apply ice candidates in a way which is async but
// in-order
this._localIceCandidatesBuffer.forEach(function(event) {
var end = !event.candidate || Object.keys(event.candidate).length == 0;
var end = !event.candidate || Object.keys(event.candidate).length === 0;
if (end) {

@@ -167,36 +177,38 @@ for (var j = 1; j < sections.length; j++) {

function(localCapabilities, remoteCapabilities) {
var commonCapabilities = {
codecs: [],
headerExtensions: [],
fecMechanisms: []
};
localCapabilities.codecs.forEach(function(lCodec) {
for (var i = 0; i < remoteCapabilities.codecs.length; i++) {
var rCodec = remoteCapabilities.codecs[i];
if (lCodec.name.toLowerCase() === rCodec.name.toLowerCase() &&
lCodec.clockRate === rCodec.clockRate &&
lCodec.numChannels === rCodec.numChannels) {
// push rCodec so we reply with offerer payload type
commonCapabilities.codecs.push(rCodec);
var commonCapabilities = {
codecs: [],
headerExtensions: [],
fecMechanisms: []
};
localCapabilities.codecs.forEach(function(lCodec) {
for (var i = 0; i < remoteCapabilities.codecs.length; i++) {
var rCodec = remoteCapabilities.codecs[i];
if (lCodec.name.toLowerCase() === rCodec.name.toLowerCase() &&
lCodec.clockRate === rCodec.clockRate &&
lCodec.numChannels === rCodec.numChannels) {
// push rCodec so we reply with offerer payload type
commonCapabilities.codecs.push(rCodec);
// FIXME: also need to determine intersection between
// .rtcpFeedback and .parameters
break;
}
}
});
// FIXME: also need to determine intersection between
// .rtcpFeedback and .parameters
break;
}
}
});
localCapabilities.headerExtensions.forEach(function(lHeaderExtension) {
for (var i = 0; i < remoteCapabilities.headerExtensions.length; i++) {
var rHeaderExtension = remoteCapabilities.headerExtensions[i];
if (lHeaderExtension.uri === rHeaderExtension.uri) {
commonCapabilities.headerExtensions.push(rHeaderExtension);
break;
}
}
});
localCapabilities.headerExtensions
.forEach(function(lHeaderExtension) {
for (var i = 0; i < remoteCapabilities.headerExtensions.length;
i++) {
var rHeaderExtension = remoteCapabilities.headerExtensions[i];
if (lHeaderExtension.uri === rHeaderExtension.uri) {
commonCapabilities.headerExtensions.push(rHeaderExtension);
break;
}
}
});
// FIXME: fecMechanisms
return commonCapabilities;
};
// FIXME: fecMechanisms
return commonCapabilities;
};

@@ -206,83 +218,88 @@ // Create ICE gatherer, ICE transport and DTLS transport.

function(mid, sdpMLineIndex) {
var self = this;
var iceGatherer = new RTCIceGatherer(self.iceOptions);
var iceTransport = new RTCIceTransport(iceGatherer);
iceGatherer.onlocalcandidate = function(evt) {
var event = new Event('icecandidate');
event.candidate = {sdpMid: mid, sdpMLineIndex: sdpMLineIndex};
var self = this;
var iceGatherer = new RTCIceGatherer(self.iceOptions);
var iceTransport = new RTCIceTransport(iceGatherer);
iceGatherer.onlocalcandidate = function(evt) {
var event = new Event('icecandidate');
event.candidate = {sdpMid: mid, sdpMLineIndex: sdpMLineIndex};
var cand = evt.candidate;
var end = !cand || Object.keys(cand).length === 0;
// Edge emits an empty object for RTCIceCandidateComplete‥
if (end) {
// polyfill since RTCIceGatherer.state is not implemented in Edge 10547 yet.
if (iceGatherer.state === undefined) {
iceGatherer.state = 'completed';
}
var cand = evt.candidate;
var end = !cand || Object.keys(cand).length === 0;
// Edge emits an empty object for RTCIceCandidateComplete‥
if (end) {
// polyfill since RTCIceGatherer.state is not implemented in
// Edge 10547 yet.
if (iceGatherer.state === undefined) {
iceGatherer.state = 'completed';
}
// Emit a candidate with type endOfCandidates to make the samples work.
// Edge requires addIceCandidate with this empty candidate to start checking.
// The real solution is to signal end-of-candidates to the other side when
// getting the null candidate but some apps (like the samples) don't do that.
event.candidate.candidate =
'candidate:1 1 udp 1 0.0.0.0 9 typ endOfCandidates';
} else {
// RTCIceCandidate doesn't have a component, needs to be added
cand.component = iceTransport.component === 'RTCP' ? 2 : 1;
event.candidate.candidate = SDPUtils.writeCandidate(cand);
}
// Emit a candidate with type endOfCandidates to make the samples
// work. Edge requires addIceCandidate with this empty candidate
// to start checking. The real solution is to signal
// end-of-candidates to the other side when getting the null
// candidate but some apps (like the samples) don't do that.
event.candidate.candidate =
'candidate:1 1 udp 1 0.0.0.0 9 typ endOfCandidates';
} else {
// RTCIceCandidate doesn't have a component, needs to be added
cand.component = iceTransport.component === 'RTCP' ? 2 : 1;
event.candidate.candidate = SDPUtils.writeCandidate(cand);
}
var complete = self.transceivers.every(function(transceiver) {
return transceiver.iceGatherer &&
transceiver.iceGatherer.state === 'completed';
});
var complete = self.transceivers.every(function(transceiver) {
return transceiver.iceGatherer &&
transceiver.iceGatherer.state === 'completed';
});
// Emit candidate if localDescription is set.
// Also emits null candidate when all gatherers are complete.
switch(self.iceGatheringState) {
case 'new':
self._localIceCandidatesBuffer.push(event);
if (end && complete) {
self._localIceCandidatesBuffer.push(new Event('icecandidate'));
}
break;
case 'gathering':
self._emitBufferedCandidates();
self.dispatchEvent(event);
if (self.onicecandidate !== null) {
self.onicecandidate(event);
}
if (complete) {
self.dispatchEvent(new Event('icecandidate'));
if (self.onicecandidate !== null) {
self.onicecandidate(new Event('icecandidate'));
// Emit candidate if localDescription is set.
// Also emits null candidate when all gatherers are complete.
switch (self.iceGatheringState) {
case 'new':
self._localIceCandidatesBuffer.push(event);
if (end && complete) {
self._localIceCandidatesBuffer.push(
new Event('icecandidate'));
}
break;
case 'gathering':
self._emitBufferedCandidates();
self.dispatchEvent(event);
if (self.onicecandidate !== null) {
self.onicecandidate(event);
}
if (complete) {
self.dispatchEvent(new Event('icecandidate'));
if (self.onicecandidate !== null) {
self.onicecandidate(new Event('icecandidate'));
}
self.iceGatheringState = 'complete';
}
break;
case 'complete':
// should not happen... currently!
break;
default: // no-op.
break;
}
self.iceGatheringState = 'complete';
}
break;
case 'complete':
// should not happen... currently!
break;
}
};
iceTransport.onicestatechange = function() {
self._updateConnectionState();
};
};
iceTransport.onicestatechange = function() {
self._updateConnectionState();
};
var dtlsTransport = new RTCDtlsTransport(iceTransport);
dtlsTransport.ondtlsstatechange = function() {
self._updateConnectionState();
};
dtlsTransport.onerror = function() {
// onerror does not set state to failed by itself.
dtlsTransport.state = 'failed';
self._updateConnectionState();
};
var dtlsTransport = new RTCDtlsTransport(iceTransport);
dtlsTransport.ondtlsstatechange = function() {
self._updateConnectionState();
};
dtlsTransport.onerror = function() {
// onerror does not set state to failed by itself.
dtlsTransport.state = 'failed';
self._updateConnectionState();
};
return {
iceGatherer: iceGatherer,
iceTransport: iceTransport,
dtlsTransport: dtlsTransport
};
};
return {
iceGatherer: iceGatherer,
iceTransport: iceTransport,
dtlsTransport: dtlsTransport
};
};

@@ -295,19 +312,19 @@ // Start the RTP Sender and Receiver for a transceiver.

if (send && transceiver.rtpSender) {
params.encodings = [{
ssrc: transceiver.sendSsrc
}];
params.encodings = transceiver.sendEncodingParameters;
params.rtcp = {
cname: SDPUtils.localCName,
ssrc: transceiver.recvSsrc
cname: SDPUtils.localCName
};
if (transceiver.recvEncodingParameters.length) {
params.rtcp.ssrc = transceiver.recvEncodingParameters[0].ssrc;
}
transceiver.rtpSender.send(params);
}
if (recv && transceiver.rtpReceiver) {
params.encodings = [{
ssrc: transceiver.recvSsrc
}];
params.encodings = transceiver.recvEncodingParameters;
params.rtcp = {
cname: transceiver.cname,
ssrc: transceiver.sendSsrc
cname: transceiver.cname
};
if (transceiver.sendEncodingParameters.length) {
params.rtcp.ssrc = transceiver.sendEncodingParameters[0].ssrc;
}
transceiver.rtpReceiver.receive(params);

@@ -319,285 +336,306 @@ }

function(description) {
var self = this;
var sections;
var sessionpart;
if (description.type === 'offer') {
if (!this._pendingOffer) {
} else {
// VERY limited support for SDP munging. Limited to:
// * changing the order of codecs
sections = SDPUtils.splitSections(description.sdp);
sessionpart = sections.shift();
sections.forEach(function(mediaSection, sdpMLineIndex) {
var caps = SDPUtils.parseRtpParameters(mediaSection);
self._pendingOffer[sdpMLineIndex].localCapabilities = caps;
});
this.transceivers = this._pendingOffer;
delete this._pendingOffer;
}
} else if (description.type === 'answer') {
sections = SDPUtils.splitSections(self.remoteDescription.sdp);
sessionpart = sections.shift();
sections.forEach(function(mediaSection, sdpMLineIndex) {
var transceiver = self.transceivers[sdpMLineIndex];
var iceGatherer = transceiver.iceGatherer;
var iceTransport = transceiver.iceTransport;
var dtlsTransport = transceiver.dtlsTransport;
var localCapabilities = transceiver.localCapabilities;
var remoteCapabilities = transceiver.remoteCapabilities;
var rejected = mediaSection.split('\n', 1)[0]
.split(' ', 2)[1] === '0';
var self = this;
var sections;
var sessionpart;
if (description.type === 'offer') {
// FIXME: What was the purpose of this empty if statement?
// if (!this._pendingOffer) {
// } else {
if (this._pendingOffer) {
// VERY limited support for SDP munging. Limited to:
// * changing the order of codecs
sections = SDPUtils.splitSections(description.sdp);
sessionpart = sections.shift();
sections.forEach(function(mediaSection, sdpMLineIndex) {
var caps = SDPUtils.parseRtpParameters(mediaSection);
self._pendingOffer[sdpMLineIndex].localCapabilities = caps;
});
this.transceivers = this._pendingOffer;
delete this._pendingOffer;
}
} else if (description.type === 'answer') {
sections = SDPUtils.splitSections(self.remoteDescription.sdp);
sessionpart = sections.shift();
sections.forEach(function(mediaSection, sdpMLineIndex) {
var transceiver = self.transceivers[sdpMLineIndex];
var iceGatherer = transceiver.iceGatherer;
var iceTransport = transceiver.iceTransport;
var dtlsTransport = transceiver.dtlsTransport;
var localCapabilities = transceiver.localCapabilities;
var remoteCapabilities = transceiver.remoteCapabilities;
var rejected = mediaSection.split('\n', 1)[0]
.split(' ', 2)[1] === '0';
if (!rejected) {
var remoteIceParameters = SDPUtils.getIceParameters(mediaSection,
sessionpart);
iceTransport.start(iceGatherer, remoteIceParameters, 'controlled');
if (!rejected) {
var remoteIceParameters = SDPUtils.getIceParameters(
mediaSection, sessionpart);
iceTransport.start(iceGatherer, remoteIceParameters,
'controlled');
var remoteDtlsParameters = SDPUtils.getDtlsParameters(mediaSection,
sessionpart);
dtlsTransport.start(remoteDtlsParameters);
var remoteDtlsParameters = SDPUtils.getDtlsParameters(
mediaSection, sessionpart);
dtlsTransport.start(remoteDtlsParameters);
// Calculate intersection of capabilities.
var params = self._getCommonCapabilities(localCapabilities,
remoteCapabilities);
// Calculate intersection of capabilities.
var params = self._getCommonCapabilities(localCapabilities,
remoteCapabilities);
// Start the RTCRtpSender. The RTCRtpReceiver for this transceiver
// has already been started in setRemoteDescription.
self._transceive(transceiver,
params.codecs.length > 0,
false);
// Start the RTCRtpSender. The RTCRtpReceiver for this
// transceiver has already been started in setRemoteDescription.
self._transceive(transceiver,
params.codecs.length > 0,
false);
}
});
}
});
}
this.localDescription = {
type: description.type,
sdp: description.sdp
};
switch (description.type) {
case 'offer':
this._updateSignalingState('have-local-offer');
break;
case 'answer':
this._updateSignalingState('stable');
break;
default:
throw new TypeError('unsupported type "' + description.type + '"');
}
this.localDescription = {
type: description.type,
sdp: description.sdp
};
switch (description.type) {
case 'offer':
this._updateSignalingState('have-local-offer');
break;
case 'answer':
this._updateSignalingState('stable');
break;
default:
throw new TypeError('unsupported type "' + description.type +
'"');
}
// If a success callback was provided, emit ICE candidates after it has been
// executed. Otherwise, emit callback after the Promise is resolved.
var hasCallback = arguments.length > 1 &&
typeof arguments[1] === 'function';
if (hasCallback) {
var cb = arguments[1];
window.setTimeout(function() {
cb();
if (self.iceGatheringState === 'new') {
self.iceGatheringState = 'gathering';
// If a success callback was provided, emit ICE candidates after it
// has been executed. Otherwise, emit callback after the Promise is
// resolved.
var hasCallback = arguments.length > 1 &&
typeof arguments[1] === 'function';
if (hasCallback) {
var cb = arguments[1];
window.setTimeout(function() {
cb();
if (self.iceGatheringState === 'new') {
self.iceGatheringState = 'gathering';
}
self._emitBufferedCandidates();
}, 0);
}
self._emitBufferedCandidates();
}, 0);
}
var p = Promise.resolve();
p.then(function() {
if (!hasCallback) {
if (self.iceGatheringState === 'new') {
self.iceGatheringState = 'gathering';
}
// Usually candidates will be emitted earlier.
window.setTimeout(self._emitBufferedCandidates.bind(self), 500);
}
});
return p;
};
var p = Promise.resolve();
p.then(function() {
if (!hasCallback) {
if (self.iceGatheringState === 'new') {
self.iceGatheringState = 'gathering';
}
// Usually candidates will be emitted earlier.
window.setTimeout(self._emitBufferedCandidates.bind(self), 500);
}
});
return p;
};
window.RTCPeerConnection.prototype.setRemoteDescription =
function(description) {
var self = this;
var stream = new MediaStream();
var receiverList = [];
var sections = SDPUtils.splitSections(description.sdp);
var sessionpart = sections.shift();
sections.forEach(function(mediaSection, sdpMLineIndex) {
var lines = SDPUtils.splitLines(mediaSection);
var mline = lines[0].substr(2).split(' ');
var kind = mline[0];
var rejected = mline[1] === '0';
var direction = SDPUtils.getDirection(mediaSection, sessionpart);
var self = this;
var stream = new MediaStream();
var receiverList = [];
var sections = SDPUtils.splitSections(description.sdp);
var sessionpart = sections.shift();
sections.forEach(function(mediaSection, sdpMLineIndex) {
var lines = SDPUtils.splitLines(mediaSection);
var mline = lines[0].substr(2).split(' ');
var kind = mline[0];
var rejected = mline[1] === '0';
var direction = SDPUtils.getDirection(mediaSection, sessionpart);
var transceiver;
var iceGatherer;
var iceTransport;
var dtlsTransport;
var rtpSender;
var rtpReceiver;
var sendSsrc;
var recvSsrc;
var localCapabilities;
var transceiver;
var iceGatherer;
var iceTransport;
var dtlsTransport;
var rtpSender;
var rtpReceiver;
var sendEncodingParameters;
var recvEncodingParameters;
var localCapabilities;
var track;
// FIXME: ensure the mediaSection has rtcp-mux set.
var remoteCapabilities = SDPUtils.parseRtpParameters(mediaSection);
var remoteIceParameters;
var remoteDtlsParameters;
if (!rejected) {
remoteIceParameters = SDPUtils.getIceParameters(mediaSection,
sessionpart);
remoteDtlsParameters = SDPUtils.getDtlsParameters(mediaSection,
sessionpart);
}
var mid = SDPUtils.matchPrefix(mediaSection, 'a=mid:')[0].substr(6);
var track;
// FIXME: ensure the mediaSection has rtcp-mux set.
var remoteCapabilities = SDPUtils.parseRtpParameters(mediaSection);
var remoteIceParameters;
var remoteDtlsParameters;
if (!rejected) {
remoteIceParameters = SDPUtils.getIceParameters(mediaSection,
sessionpart);
remoteDtlsParameters = SDPUtils.getDtlsParameters(mediaSection,
sessionpart);
}
recvEncodingParameters =
SDPUtils.parseRtpEncodingParameters(mediaSection);
var cname;
// Gets the first SSRC. Note that with RTX there might be multiple SSRCs.
var remoteSsrc = SDPUtils.matchPrefix(mediaSection, 'a=ssrc:')
.map(function(line) {
return SDPUtils.parseSsrcMedia(line);
})
.filter(function(obj) {
return obj.attribute === 'cname';
})[0];
if (remoteSsrc) {
recvSsrc = parseInt(remoteSsrc.ssrc, 10);
cname = remoteSsrc.value;
}
var mid = SDPUtils.matchPrefix(mediaSection, 'a=mid:');
if (mid.length) {
mid = mid[0].substr(6);
} else {
mid = SDPUtils.generateIdentifier();
}
var isComplete = SDPUtils.matchPrefix(mediaSection,
'a=end-of-candidates').length > 0;
var cands = SDPUtils.matchPrefix(mediaSection, 'a=candidate:')
.map(function(cand) {
return SDPUtils.parseCandidate(cand);
})
.filter(function(cand) {
return cand.component === '1';
});
if (description.type === 'offer') {
var transports = self._createIceAndDtlsTransports(mid, sdpMLineIndex);
if (isComplete) {
transports.iceTransport.setRemoteCandidates(cands);
}
var cname;
// Gets the first SSRC. Note that with RTX there might be multiple
// SSRCs.
var remoteSsrc = SDPUtils.matchPrefix(mediaSection, 'a=ssrc:')
.map(function(line) {
return SDPUtils.parseSsrcMedia(line);
})
.filter(function(obj) {
return obj.attribute === 'cname';
})[0];
if (remoteSsrc) {
cname = remoteSsrc.value;
}
localCapabilities = RTCRtpReceiver.getCapabilities(kind);
sendSsrc = (2 * sdpMLineIndex + 2) * 1001;
var isComplete = SDPUtils.matchPrefix(mediaSection,
'a=end-of-candidates').length > 0;
var cands = SDPUtils.matchPrefix(mediaSection, 'a=candidate:')
.map(function(cand) {
return SDPUtils.parseCandidate(cand);
})
.filter(function(cand) {
return cand.component === '1';
});
if (description.type === 'offer' && !rejected) {
var transports = self._createIceAndDtlsTransports(mid,
sdpMLineIndex);
if (isComplete) {
transports.iceTransport.setRemoteCandidates(cands);
}
rtpReceiver = new RTCRtpReceiver(transports.dtlsTransport, kind);
localCapabilities = RTCRtpReceiver.getCapabilities(kind);
sendEncodingParameters = [{
ssrc: (2 * sdpMLineIndex + 2) * 1001
}];
track = rtpReceiver.track;
receiverList.push([track, rtpReceiver]);
// FIXME: not correct when there are multiple streams but that is
// not currently supported in this shim.
stream.addTrack(track);
rtpReceiver = new RTCRtpReceiver(transports.dtlsTransport, kind);
// FIXME: look at direction.
if (self.localStreams.length > 0 &&
self.localStreams[0].getTracks().length >= sdpMLineIndex) {
// FIXME: actually more complicated, needs to match types etc
var localtrack = self.localStreams[0].getTracks()[sdpMLineIndex];
rtpSender = new RTCRtpSender(localtrack, transports.dtlsTransport);
}
track = rtpReceiver.track;
receiverList.push([track, rtpReceiver]);
// FIXME: not correct when there are multiple streams but that is
// not currently supported in this shim.
stream.addTrack(track);
self.transceivers[sdpMLineIndex] = {
iceGatherer: transports.iceGatherer,
iceTransport: transports.iceTransport,
dtlsTransport: transports.dtlsTransport,
localCapabilities: localCapabilities,
remoteCapabilities: remoteCapabilities,
rtpSender: rtpSender,
rtpReceiver: rtpReceiver,
kind: kind,
mid: mid,
cname: cname,
sendSsrc: sendSsrc,
recvSsrc: recvSsrc
};
// Start the RTCRtpReceiver now. The RTPSender is started in setLocalDescription.
self._transceive(self.transceivers[sdpMLineIndex],
false,
direction === 'sendrecv' || direction === 'sendonly');
} else if (description.type === 'answer' && !rejected) {
transceiver = self.transceivers[sdpMLineIndex];
iceGatherer = transceiver.iceGatherer;
iceTransport = transceiver.iceTransport;
dtlsTransport = transceiver.dtlsTransport;
rtpSender = transceiver.rtpSender;
rtpReceiver = transceiver.rtpReceiver;
sendSsrc = transceiver.sendSsrc;
//recvSsrc = transceiver.recvSsrc;
localCapabilities = transceiver.localCapabilities;
// FIXME: look at direction.
if (self.localStreams.length > 0 &&
self.localStreams[0].getTracks().length >= sdpMLineIndex) {
// FIXME: actually more complicated, needs to match types etc
var localtrack = self.localStreams[0]
.getTracks()[sdpMLineIndex];
rtpSender = new RTCRtpSender(localtrack,
transports.dtlsTransport);
}
self.transceivers[sdpMLineIndex].recvSsrc = recvSsrc;
self.transceivers[sdpMLineIndex].remoteCapabilities =
remoteCapabilities;
self.transceivers[sdpMLineIndex].cname = cname;
self.transceivers[sdpMLineIndex] = {
iceGatherer: transports.iceGatherer,
iceTransport: transports.iceTransport,
dtlsTransport: transports.dtlsTransport,
localCapabilities: localCapabilities,
remoteCapabilities: remoteCapabilities,
rtpSender: rtpSender,
rtpReceiver: rtpReceiver,
kind: kind,
mid: mid,
cname: cname,
sendEncodingParameters: sendEncodingParameters,
recvEncodingParameters: recvEncodingParameters
};
// Start the RTCRtpReceiver now. The RTPSender is started in
// setLocalDescription.
self._transceive(self.transceivers[sdpMLineIndex],
false,
direction === 'sendrecv' || direction === 'sendonly');
} else if (description.type === 'answer' && !rejected) {
transceiver = self.transceivers[sdpMLineIndex];
iceGatherer = transceiver.iceGatherer;
iceTransport = transceiver.iceTransport;
dtlsTransport = transceiver.dtlsTransport;
rtpSender = transceiver.rtpSender;
rtpReceiver = transceiver.rtpReceiver;
sendEncodingParameters = transceiver.sendEncodingParameters;
localCapabilities = transceiver.localCapabilities;
if (isComplete) {
iceTransport.setRemoteCandidates(cands);
}
iceTransport.start(iceGatherer, remoteIceParameters, 'controlling');
dtlsTransport.start(remoteDtlsParameters);
self.transceivers[sdpMLineIndex].recvEncodingParameters =
recvEncodingParameters;
self.transceivers[sdpMLineIndex].remoteCapabilities =
remoteCapabilities;
self.transceivers[sdpMLineIndex].cname = cname;
self._transceive(transceiver,
direction === 'sendrecv' || direction === 'recvonly',
direction === 'sendrecv' || direction === 'sendonly');
if (isComplete) {
iceTransport.setRemoteCandidates(cands);
}
iceTransport.start(iceGatherer, remoteIceParameters,
'controlling');
dtlsTransport.start(remoteDtlsParameters);
if (rtpReceiver &&
(direction === 'sendrecv' || direction === 'sendonly')) {
track = rtpReceiver.track;
receiverList.push([track, rtpReceiver]);
stream.addTrack(track);
} else {
// FIXME: actually the receiver should be created later.
delete transceiver.rtpReceiver;
self._transceive(transceiver,
direction === 'sendrecv' || direction === 'recvonly',
direction === 'sendrecv' || direction === 'sendonly');
if (rtpReceiver &&
(direction === 'sendrecv' || direction === 'sendonly')) {
track = rtpReceiver.track;
receiverList.push([track, rtpReceiver]);
stream.addTrack(track);
} else {
// FIXME: actually the receiver should be created later.
delete transceiver.rtpReceiver;
}
}
});
this.remoteDescription = {
type: description.type,
sdp: description.sdp
};
switch (description.type) {
case 'offer':
this._updateSignalingState('have-remote-offer');
break;
case 'answer':
this._updateSignalingState('stable');
break;
default:
throw new TypeError('unsupported type "' + description.type +
'"');
}
}
});
if (stream.getTracks().length) {
self.remoteStreams.push(stream);
window.setTimeout(function() {
var event = new Event('addstream');
event.stream = stream;
self.dispatchEvent(event);
if (self.onaddstream !== null) {
window.setTimeout(function() {
self.onaddstream(event);
}, 0);
}
this.remoteDescription = {
type: description.type,
sdp: description.sdp
};
switch (description.type) {
case 'offer':
this._updateSignalingState('have-remote-offer');
break;
case 'answer':
this._updateSignalingState('stable');
break;
default:
throw new TypeError('unsupported type "' + description.type + '"');
}
if (stream.getTracks().length) {
self.remoteStreams.push(stream);
window.setTimeout(function() {
var event = new Event('addstream');
event.stream = stream;
self.dispatchEvent(event);
if (self.onaddstream !== null) {
window.setTimeout(function() {
self.onaddstream(event);
receiverList.forEach(function(item) {
var track = item[0];
var receiver = item[1];
var trackEvent = new Event('track');
trackEvent.track = track;
trackEvent.receiver = receiver;
trackEvent.streams = [stream];
self.dispatchEvent(event);
if (self.ontrack !== null) {
window.setTimeout(function() {
self.ontrack(trackEvent);
}, 0);
}
});
}, 0);
}
if (arguments.length > 1 && typeof arguments[1] === 'function') {
window.setTimeout(arguments[1], 0);
}
return Promise.resolve();
};
receiverList.forEach(function(item) {
var track = item[0];
var receiver = item[1];
var event = new Event('track');
event.track = track;
event.receiver = receiver;
event.streams = [stream];
self.dispatchEvent(event);
if (self.ontrack !== null) {
window.setTimeout(function() {
self.ontrack(event);
}, 0);
}
});
}, 0);
}
if (arguments.length > 1 && typeof arguments[1] === 'function') {
window.setTimeout(arguments[1], 0);
}
return Promise.resolve();
};
window.RTCPeerConnection.prototype.close = function() {

@@ -630,9 +668,9 @@ this.transceivers.forEach(function(transceiver) {

function(newState) {
this.signalingState = newState;
var event = new Event('signalingstatechange');
this.dispatchEvent(event);
if (this.onsignalingstatechange !== null) {
this.onsignalingstatechange(event);
}
};
this.signalingState = newState;
var event = new Event('signalingstatechange');
this.dispatchEvent(event);
if (this.onsignalingstatechange !== null) {
this.onsignalingstatechange(event);
}
};

@@ -642,13 +680,12 @@ // Determine whether to fire the negotiationneeded event.

function() {
// Fire away (for now).
var event = new Event('negotiationneeded');
this.dispatchEvent(event);
if (this.onnegotiationneeded !== null) {
this.onnegotiationneeded(event);
}
};
// Fire away (for now).
var event = new Event('negotiationneeded');
this.dispatchEvent(event);
if (this.onnegotiationneeded !== null) {
this.onnegotiationneeded(event);
}
};
// Update the connection state.
window.RTCPeerConnection.prototype._updateConnectionState =
function() {
window.RTCPeerConnection.prototype._updateConnectionState = function() {
var self = this;

@@ -670,14 +707,14 @@ var newState;

// ICETransport.completed and connected are the same for this purpose.
states['connected'] += states['completed'];
states.connected += states.completed;
newState = 'new';
if (states['failed'] > 0) {
if (states.failed > 0) {
newState = 'failed';
} else if (states['connecting'] > 0 || states['checking'] > 0) {
} else if (states.connecting > 0 || states.checking > 0) {
newState = 'connecting';
} else if (states['disconnected'] > 0) {
} else if (states.disconnected > 0) {
newState = 'disconnected';
} else if (states['new'] > 0) {
} else if (states.new > 0) {
newState = 'new';
} else if (states['connecting'] > 0 || states['completed'] > 0) {
} else if (states.connected > 0 || states.completed > 0) {
newState = 'connected';

@@ -767,4 +804,4 @@ }

tracks.forEach(function(mline, sdpMLineIndex) {
// For each track, create an ice gatherer, ice transport, dtls transport,
// potentially rtpsender and rtpreceiver.
// For each track, create an ice gatherer, ice transport,
// dtls transport, potentially rtpsender and rtpreceiver.
var track = mline.track;

@@ -781,3 +818,5 @@ var kind = mline.kind;

// generate an ssrc now, to be used later in rtpSender.send
var sendSsrc = (2 * sdpMLineIndex + 1) * 1001;
var sendEncodingParameters = [{
ssrc: (2 * sdpMLineIndex + 1) * 1001
}];
if (track) {

@@ -801,4 +840,4 @@ rtpSender = new RTCRtpSender(track, transports.dtlsTransport);

mid: mid,
sendSsrc: sendSsrc,
recvSsrc: null
sendEncodingParameters: sendEncodingParameters,
recvEncodingParameters: null
};

@@ -823,8 +862,2 @@ var transceiver = transceivers[sdpMLineIndex];

var self = this;
var answerOptions;
if (arguments.length === 1 && typeof arguments[0] !== 'function') {
answerOptions = arguments[0];
} else if (arguments.length === 3) {
answerOptions = arguments[2];
}

@@ -897,6 +930,6 @@ var sdp = SDPUtils.writeSessionBoilerplate();

'dtlsTransport'].forEach(function(method) {
if (transceiver[method]) {
promises.push(transceiver[method].getStats());
}
});
if (transceiver[method]) {
promises.push(transceiver[method].getStats());
}
});
});

@@ -903,0 +936,0 @@ var cb = arguments.length > 1 && typeof arguments[1] === 'function' &&

@@ -8,2 +8,3 @@ /*

*/
/* eslint-env node */
'use strict';

@@ -19,3 +20,5 @@

Object.defineProperty(window.RTCPeerConnection.prototype, 'ontrack', {
get: function() { return this._ontrack; },
get: function() {
return this._ontrack;
},
set: function(f) {

@@ -88,3 +91,3 @@ if (this._ontrack) {

}
return new mozRTCPeerConnection(pcConfig, pcConstraints); // jscs:ignore requireCapitalizedConstructors
return new mozRTCPeerConnection(pcConfig, pcConstraints);
};

@@ -97,8 +100,3 @@ window.RTCPeerConnection.prototype = mozRTCPeerConnection.prototype;

get: function() {
if (arguments.length) {
return mozRTCPeerConnection.generateCertificate.apply(null,
arguments);
} else {
return mozRTCPeerConnection.generateCertificate;
}
return mozRTCPeerConnection.generateCertificate;
}

@@ -111,2 +109,13 @@ });

}
// shim away need for obsolete RTCIceCandidate/RTCSessionDescription.
['setLocalDescription', 'setRemoteDescription', 'addIceCandidate']
.forEach(function(method) {
var nativeMethod = RTCPeerConnection.prototype[method];
RTCPeerConnection.prototype[method] = function() {
arguments[0] = new ((method === 'addIceCandidate')?
RTCIceCandidate : RTCSessionDescription)(arguments[0]);
return nativeMethod.apply(this, arguments);
};
});
},

@@ -161,2 +170,3 @@

};
constraints = JSON.parse(JSON.stringify(constraints));
if (browserDetails.version < 38) {

@@ -193,10 +203,10 @@ logging('spec: ' + JSON.stringify(constraints));

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);
});
};
return new Promise(function(resolve) {
var infos = [
{kind: 'audioinput', deviceId: 'default', label: '', groupId: ''},
{kind: 'videoinput', deviceId: 'default', label: '', groupId: ''}
];
resolve(infos);
});
};

@@ -203,0 +213,0 @@ if (browserDetails.version < 41) {

@@ -8,2 +8,3 @@ /*

*/
/* eslint-env node */
'use strict';

@@ -61,2 +62,3 @@

};
constraints = JSON.parse(JSON.stringify(constraints));
if (browserDetails.version < 38) {

@@ -93,10 +95,10 @@ logging('spec: ' + JSON.stringify(constraints));

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);
});
};
return new Promise(function(resolve) {
var infos = [
{kind: 'audioinput', deviceId: 'default', label: '', groupId: ''},
{kind: 'videoinput', deviceId: 'default', label: '', groupId: ''}
];
resolve(infos);
});
};

@@ -103,0 +105,0 @@ if (browserDetails.version < 41) {

@@ -8,2 +8,3 @@ /*

*/
/* eslint-env node */
'use strict';

@@ -30,3 +31,5 @@

}
console.log.apply(console, arguments);
if (typeof console !== 'undefined' && typeof console.log === 'function') {
console.log.apply(console, arguments);
}
}

@@ -61,7 +64,10 @@ },

// Fail early if it's not a browser
if (typeof window === 'undefined' || !window.navigator) {
result.browser = 'Not a browser.';
return result;
} else if (navigator.mozGetUserMedia) {
// Firefox.
}
// Firefox.
if (navigator.mozGetUserMedia) {
result.browser = 'firefox';

@@ -71,11 +77,43 @@ result.version = this.extractVersion(navigator.userAgent,

result.minVersion = 31;
} else if (navigator.webkitGetUserMedia && window.webkitRTCPeerConnection) {
// Chrome, Chromium, WebView, Opera and other WebKit browsers.
result.browser = 'chrome';
result.version = this.extractVersion(navigator.userAgent,
// all webkit-based browsers
} else if (navigator.webkitGetUserMedia) {
// Chrome, Chromium, Webview, Opera, all use the chrome shim for now
if (window.webkitRTCPeerConnection) {
result.browser = 'chrome';
result.version = this.extractVersion(navigator.userAgent,
/Chrom(e|ium)\/([0-9]+)\./, 2);
result.minVersion = 38;
} else if(navigator.mediaDevices &&
result.minVersion = 38;
// Safari or unknown webkit-based
// for the time being Safari has support for MediaStreams but not webRTC
} else {
// Safari UA substrings of interest for reference:
// - webkit version: AppleWebKit/602.1.25 (also used in Op,Cr)
// - safari UI version: Version/9.0.3 (unique to Safari)
// - safari UI webkit version: Safari/601.4.4 (also used in Op,Cr)
//
// if the webkit version and safari UI webkit versions are equals,
// ... this is a stable version.
//
// only the internal webkit version is important today to know if
// media streams are supported
//
if (navigator.userAgent.match(/Version\/(\d+).(\d+)/)) {
result.browser = 'safari';
result.version = this.extractVersion(navigator.userAgent,
/AppleWebKit\/([0-9]+)\./, 1);
result.minVersion = 602;
// unknown webkit-based browser
} else {
result.browser = 'Unsupported webkit-based browser ' +
'with GUM support but no WebRTC support.';
return result;
}
}
// Edge.
} else if (navigator.mediaDevices &&
navigator.userAgent.match(/Edge\/(\d+).(\d+)$/)) {
// Edge.
result.browser = 'edge';

@@ -85,2 +123,4 @@ result.version = this.extractVersion(navigator.userAgent,

result.minVersion = 10547;
// Default fallthrough: not supported.
} else {

@@ -87,0 +127,0 @@ result.browser = 'Not a supported browser.';

@@ -8,8 +8,42 @@ /*

*/
/* jshint node: true */
/* globals require */
/* eslint-env node */
'use strict';
var fs = require('fs');
var os = require('os');
var test = require('tape');
if (!process.env.BROWSER) {
process.env.BROWSER = 'chrome';
}
if (!process.env.BVER) {
process.env.BVER = 'stable';
}
var browserbin = './browsers/bin/' + process.env.BROWSER +
'-' + process.env.BVER;
// install browsers via travis-multirunner (on Linux).
if (os.platform() === 'linux') {
try {
fs.accessSync(browserbin, fs.X_OK);
} catch (e) {
if (e.code === 'ENOENT') {
// execute travis-multirunner setup to install browser
require('child_process').execSync(
'./node_modules/travis-multirunner/setup.sh');
}
}
}
if (os.platform() === 'win32') {
if (process.env.BROWSER === 'MicrosoftEdge') {
// assume MicrosoftWebDriver is installed.
process.env.PATH += ';C:\\Program Files (x86)\\Microsoft Web Driver\\';
}
if (process.env.BROWSER === 'chrome') {
// for some reason chromedriver doesn't like the one in node_modules\.bin
process.env.PATH += ';' + process.cwd() +
'\\node_modules\\chromedriver\\lib\\chromedriver\\';
}
}
// Add all test files here with a short comment.

@@ -16,0 +50,0 @@

@@ -15,13 +15,169 @@ /*

var videoSDP = 'v=0\r\no=- 1376706046264470145 3 IN IP4 127.0.0.1\r\ns=-\r\nt=0 0\r\na=group:BUNDLE video\r\na=msid-semantic: WMS EZVtYL50wdbfttMdmVFITVoKc4XgA0KBZXzd\r\nm=video 9 UDP/TLS/RTP/SAVPF 100 101 107 116 117 96 97 99 98\r\nc=IN IP4 0.0.0.0\r\na=rtcp:9 IN IP4 0.0.0.0\r\na=ice-ufrag:npaLWmWDg3Yp6vJt\r\na=ice-pwd:pdfQZAiFbcsFmUKWw55g4TD5\r\na=fingerprint:sha-256 3D:05:43:01:66:AC:57:DC:17:55:08:5C:D4:25:D7:CA:FD:E1:0E:C1:F4:F8:43:3E:10:CE:3E:E7:6E:20:B9:90\r\na=setup:actpass\r\na=mid:video\r\na=extmap:2 urn:ietf:params:rtp-hdrext:toffset\r\na=extmap:3 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time\r\na=extmap:4 urn:3gpp:video-orientation\r\na=sendrecv\r\na=rtcp-mux\r\na=rtcp-rsize\r\na=rtpmap:100 VP8/90000\r\na=rtcp-fb:100 ccm fir\r\na=rtcp-fb:100 nack\r\na=rtcp-fb:100 nack pli\r\na=rtcp-fb:100 goog-remb\r\na=rtcp-fb:100 transport-cc\r\na=rtpmap:101 VP9/90000\r\na=rtcp-fb:101 ccm fir\r\na=rtcp-fb:101 nack\r\na=rtcp-fb:101 nack pli\r\na=rtcp-fb:101 goog-remb\r\na=rtcp-fb:101 transport-cc\r\na=rtpmap:107 H264/90000\r\na=rtcp-fb:107 ccm fir\r\na=rtcp-fb:107 nack\r\na=rtcp-fb:107 nack pli\r\na=rtcp-fb:107 goog-remb\r\na=rtcp-fb:107 transport-cc\r\na=rtpmap:116 red/90000\r\na=rtpmap:117 ulpfec/90000\r\na=rtpmap:96 rtx/90000\r\na=fmtp:96 apt=100\r\na=rtpmap:97 rtx/90000\r\na=fmtp:97 apt=101\r\na=rtpmap:99 rtx/90000\r\na=fmtp:99 apt=107\r\na=rtpmap:98 rtx/90000\r\na=fmtp:98 apt=116\r\na=ssrc-group:FID 1734522595 2715962409\r\na=ssrc:1734522595 cname:VrveQctHgkwqDKj6\r\na=ssrc:1734522595 msid:EZVtYL50wdbfttMdmVFITVoKc4XgA0KBZXzd 63238d63-9a20-4afc-832c-48678926afce\r\na=ssrc:1734522595 mslabel:EZVtYL50wdbfttMdmVFITVoKc4XgA0KBZXzd\r\na=ssrc:1734522595 label:63238d63-9a20-4afc-832c-48678926afce\r\na=ssrc:2715962409 cname:VrveQctHgkwqDKj6\r\na=ssrc:2715962409 msid:EZVtYL50wdbfttMdmVFITVoKc4XgA0KBZXzd 63238d63-9a20-4afc-832c-48678926afce\r\na=ssrc:2715962409 mslabel:EZVtYL50wdbfttMdmVFITVoKc4XgA0KBZXzd\r\na=ssrc:2715962409 label:63238d63-9a20-4afc-832c-48678926afce\r\n'; // jscs: disable
var videoSDP =
'v=0\r\no=- 1376706046264470145 3 IN IP4 127.0.0.1\r\ns=-\r\n' +
't=0 0\r\na=group:BUNDLE video\r\n' +
'a=msid-semantic: WMS EZVtYL50wdbfttMdmVFITVoKc4XgA0KBZXzd\r\n' +
'm=video 9 UDP/TLS/RTP/SAVPF 100 101 107 116 117 96 97 99 98\r\n' +
'c=IN IP4 0.0.0.0\r\na=rtcp:9 IN IP4 0.0.0.0\r\n' +
'a=ice-ufrag:npaLWmWDg3Yp6vJt\r\na=ice-pwd:pdfQZAiFbcsFmUKWw55g4TD5\r\n' +
'a=fingerprint:sha-256 3D:05:43:01:66:AC:57:DC:17:55:08:5C:D4:25:D7:CA:FD' +
':E1:0E:C1:F4:F8:43:3E:10:CE:3E:E7:6E:20:B9:90\r\n' +
'a=setup:actpass\r\na=mid:video\r\n' +
'a=extmap:2 urn:ietf:params:rtp-hdrext:toffset\r\n' +
'a=extmap:3 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time\r\n' +
'a=extmap:4 urn:3gpp:video-orientation\r\na=sendrecv\r\na=rtcp-mux\r\n' +
'a=rtcp-rsize\r\na=rtpmap:100 VP8/90000\r\n' +
'a=rtcp-fb:100 ccm fir\r\na=rtcp-fb:100 nack\r\na=rtcp-fb:100 nack pli\r\n' +
'a=rtcp-fb:100 goog-remb\r\na=rtcp-fb:100 transport-cc\r\n' +
'a=rtpmap:101 VP9/90000\r\na=rtcp-fb:101 ccm fir\r\na=rtcp-fb:101 nack\r\n' +
'a=rtcp-fb:101 nack pli\r\na=rtcp-fb:101 goog-remb\r\n' +
'a=rtcp-fb:101 transport-cc\r\na=rtpmap:107 H264/90000\r\n' +
'a=rtcp-fb:107 ccm fir\r\na=rtcp-fb:107 nack\r\na=rtcp-fb:107 nack pli\r\n' +
'a=rtcp-fb:107 goog-remb\r\na=rtcp-fb:107 transport-cc\r\n' +
'a=rtpmap:116 red/90000\r\na=rtpmap:117 ulpfec/90000\r\n' +
'a=rtpmap:96 rtx/90000\r\na=fmtp:96 apt=100\r\na=rtpmap:97 rtx/90000\r\n' +
'a=fmtp:97 apt=101\r\na=rtpmap:99 rtx/90000\r\na=fmtp:99 apt=107\r\n' +
'a=rtpmap:98 rtx/90000\r\na=fmtp:98 apt=116\r\n' +
'a=ssrc-group:FID 1734522595 2715962409\r\n' +
'a=ssrc:1734522595 cname:VrveQctHgkwqDKj6\r\n' +
'a=ssrc:1734522595 msid:EZVtYL50wdbfttMdmVFITVoKc4XgA0KBZXzd ' +
'63238d63-9a20-4afc-832c-48678926afce\r\na=ssrc:1734522595 ' +
'mslabel:EZVtYL50wdbfttMdmVFITVoKc4XgA0KBZXzd\r\n' +
'a=ssrc:1734522595 label:63238d63-9a20-4afc-832c-48678926afce\r\n' +
'a=ssrc:2715962409 cname:VrveQctHgkwqDKj6\r\n' +
'a=ssrc:2715962409 msid:EZVtYL50wdbfttMdmVFITVoKc4XgA0KBZXzd ' +
'63238d63-9a20-4afc-832c-48678926afce\r\n' +
'a=ssrc:2715962409 mslabel:EZVtYL50wdbfttMdmVFITVoKc4XgA0KBZXzd\r\n' +
'a=ssrc:2715962409 label:63238d63-9a20-4afc-832c-48678926afce\r\n';
// Firefox offer
var videoSDP2 =
'v=0\r\n' +
'o=mozilla...THIS_IS_SDPARTA-45.0 5508396880163053452 0 IN IP4 0.0.0.0\r\n' +
's=-\r\nt=0 0\r\n' +
'a=fingerprint:sha-256 CC:0D:FB:A8:9F:59:36:57:69:F6:2C:0E:A3:EA:19:5A:E0' +
':D4:37:82:D4:7B:FB:94:3D:F6:0E:F8:29:A7:9E:9C\r\n' +
'a=ice-options:trickle\r\na=msid-semantic:WMS *\r\n' +
'm=video 9 UDP/TLS/RTP/SAVPF 120 126 97\r\n' +
'c=IN IP4 0.0.0.0\r\na=sendrecv\r\n' +
'a=fmtp:126 profile-level-id=42e01f;level-asymmetry-allowed=1;' +
'packetization-mode=1\r\n' +
'a=fmtp:97 profile-level-id=42e01f;level-asymmetry-allowed=1\r\n' +
'a=fmtp:120 max-fs=12288;max-fr=60\r\n' +
'a=ice-pwd:e81aeca45422c37aeb669274d8959200\r\n' +
'a=ice-ufrag:30607a5c\r\na=mid:sdparta_0\r\n' +
'a=msid:{782ddf65-d10e-4dad-80b9-27e9f3928d82} ' +
'{37802bbd-01e2-481e-a2e8-acb5423b7a55}\r\n' +
'a=rtcp-fb:120 nack\r\na=rtcp-fb:120 nack pli\r\na=rtcp-fb:120 ccm fir\r\n' +
'a=rtcp-fb:126 nack\r\na=rtcp-fb:126 nack pli\r\na=rtcp-fb:126 ccm fir\r\n' +
'a=rtcp-fb:97 nack\r\na=rtcp-fb:97 nack pli\r\na=rtcp-fb:97 ccm fir\r\n' +
'a=rtcp-mux\r\na=rtpmap:120 VP8/90000\r\na=rtpmap:126 H264/90000\r\n' +
'a=rtpmap:97 H264/90000\r\na=setup:actpass\r\n' +
'a=ssrc:98927270 cname:{0817e909-53be-4a3f-ac45-b5a0e5edc3a7}\r\n';
test('splitSections', function(t) {
var parsed = SDPUtils.splitSections(videoSDP.replace(/\r\n/g, '\n'));
t.ok(parsed.length === 2,
'split video-only SDP with only LF into two sections');
parsed = SDPUtils.splitSections(videoSDP);
t.ok(parsed.length === 2, 'split video-only SDP into two sections');
t.ok(parsed.every(function(section) {
return section.substr(-2) === '\r\n';
}), 'every section ends with CRLF');
t.ok(parsed.join('') === videoSDP,
'joining sections without separator recreates SDP');
t.end();
});
test('parseRtpParameters', function(t) {
var sections = SDPUtils.splitSections(videoSDP);
var data = SDPUtils.parseRtpParameters(sections[1]);
t.ok(data.codecs.length === 9, 'parsed 9 codecs');
t.ok(data.fecMechanisms.length === 2, 'parsed FEC mechanisms');
t.ok(data.fecMechanisms.indexOf('RED') !== -1, 'parsed RED as FEC mechanism');
t.ok(data.fecMechanisms.indexOf('ULPFEC') !== -1, 'parsed ULPFEC as FEC mechanism');
t.ok(data.headerExtensions.length === 3, 'parsed 3 header extensions');
var parsed = SDPUtils.parseRtpParameters(sections[1]);
t.ok(parsed.codecs.length === 9, 'parsed 9 codecs');
t.ok(parsed.fecMechanisms.length === 2, 'parsed FEC mechanisms');
t.ok(parsed.fecMechanisms.indexOf('RED') !== -1,
'parsed RED as FEC mechanism');
t.ok(parsed.fecMechanisms.indexOf('ULPFEC') !== -1,
'parsed ULPFEC as FEC mechanism');
t.ok(parsed.headerExtensions.length === 3, 'parsed 3 header extensions');
t.end();
});
test('fmtp parsing and serialization', function(t) {
var line = 'a=fmtp:111 minptime=10; useinbandfec=1';
var parsed = SDPUtils.parseFmtp(line);
t.ok(Object.keys(parsed).length === 2, 'parsed 2 parameters');
t.ok(parsed.minptime === '10', 'parsed minptime');
t.ok(parsed.useinbandfec === '1', 'parsed useinbandfec');
// TODO: is this safe or can the order change?
// serialization strings the extra whitespace after ';'
t.ok(SDPUtils.writeFmtp({payloadType: 111, parameters: parsed})
=== line.replace('; ', ';') + '\r\n',
'serialization does not add extra spaces between parameters');
t.end();
});
test('rtpmap parsing and serialization', function(t) {
var line = 'a=rtpmap:111 opus/48000/2';
var parsed = SDPUtils.parseRtpMap(line);
t.ok(parsed.name === 'opus', 'parsed codec name');
t.ok(parsed.payloadType === 111, 'parsed payloadType as integer');
t.ok(parsed.clockRate === 48000, 'parsed clockRate as integer');
t.ok(parsed.numChannels === 2, 'parsed numChannels');
parsed = SDPUtils.parseRtpMap('a=rtpmap:0 PCMU/8000');
t.ok(parsed.numChannels === 1, 'numChannels defaults to 1 if not present');
t.ok(SDPUtils.writeRtpMap({
payloadType: 111,
name: 'opus',
clockRate: 48000,
numChannels: 2
}).trim() === line, 'serialized rtpmap');
t.end();
});
test('parseRtpEncodingParameters', function(t) {
var sections = SDPUtils.splitSections(videoSDP);
var data = SDPUtils.parseRtpEncodingParameters(sections[1]);
t.ok(data.length === 8, 'parsed encoding parameters for four codecs');
t.ok(data[0].ssrc === 1734522595, 'parsed primary SSRC');
t.ok(data[0].rtx, 'has RTX encoding');
t.ok(data[0].rtx.ssrc === 2715962409, 'parsed secondary SSRC for RTX');
t.end();
});
test('parseRtpEncodingParameters fallback', function(t) {
var sections = SDPUtils.splitSections(videoSDP2);
var data = SDPUtils.parseRtpEncodingParameters(sections[1]);
t.ok(data.length === 1 && data[0].ssrc === 98927270, 'parsed single SSRC');
t.end();
});
test('parseRtpEncodingParameters with b=AS', function(t) {
var sections = SDPUtils.splitSections(
videoSDP.replace('c=IN IP4 0.0.0.0\r\n',
'c=IN IP4 0.0.0.0\r\nb=AS:512\r\n')
);
var data = SDPUtils.parseRtpEncodingParameters(sections[1]);
t.ok(data[0].maxBitrate === 512, 'parsed b=AS:512');
t.end();
});
test('parseRtpEncodingParameters with b=TIAS', function(t) {
var sections = SDPUtils.splitSections(
videoSDP.replace('c=IN IP4 0.0.0.0\r\n',
'c=IN IP4 0.0.0.0\r\nb=TIAS:512\r\n')
);
var data = SDPUtils.parseRtpEncodingParameters(sections[1]);
t.ok(data[1].maxBitrate === 512, 'parsed b=AS:512');
t.end();
});

@@ -8,6 +8,5 @@ /*

*/
/* jshint node: true */
/* eslint-env node */
'use strict';
'use strict';
// https://code.google.com/p/selenium/wiki/WebDriverJs

@@ -18,2 +17,3 @@ var webdriver = require('selenium-webdriver');

var fs = require('fs');
var os = require('os');

@@ -34,4 +34,4 @@ var sharedDriver = null;

var browserVersion = function(path, expr, pos) {
var match = path.match(expr);
var browserVersion = function(pathToBrowser, expr, pos) {
var match = pathToBrowser.match(expr);
return match && match.length >= pos && parseInt(match[pos], 10);

@@ -68,4 +68,6 @@ };

var firefoxOptions = new firefox.Options()
.setProfile(profile)
.setBinary('node_modules/.bin/start-firefox');
.setProfile(profile);
if (os.platform() === 'linux') {
firefoxOptions.setBinary('node_modules/.bin/start-firefox');
}

@@ -75,6 +77,8 @@ // Chrome options.

var chromeOptions = new chrome.Options()
.setChromeBinaryPath('node_modules/.bin/start-chrome')
.addArguments('allow-file-access-from-files')
.addArguments('use-fake-device-for-media-stream')
.addArguments('use-fake-ui-for-media-stream');
if (os.platform() === 'linux') {
chromeOptions.setChromeBinaryPath('node_modules/.bin/start-chrome');
}

@@ -81,0 +85,0 @@ // Only enable this for Chrome >= 49.

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc