diff --git a/README.md b/README.md index 12947073..71644ff0 100644 --- a/README.md +++ b/README.md @@ -275,13 +275,13 @@ Whatever app you are developing, chances are you will need to pass some data for - **API REST**: when asking for a token to */newToken*, you can pass data as third parameter in the BODY of the POST request ``` - {“0”: “sessionId”, “1”: “role”, “2”: "DATA"} + {“session”: “sessionId”, “role”: “role”, “data”: "DATA"} ``` > **openvidu-backend-client** allows you to pass data when creating a Token object:
> `Token t = new TokenOptions.Builder().data("DATA").build();` -The result will be that in all clients *Connection* objects will have in their *data* property the pertinent value you have provided for each user. So, an easy way to get the data associated to any user would be: +The result will be that in all clients, *Connection* objects will have in their *data* property the pertinent value you have provided for each user. So, an easy way to get the data associated to any user would be: ```javascript session.on('streamCreated', function (event) { diff --git a/openvidu-browser/src/main/resources/static/js/OpenVidu.js b/openvidu-browser/src/main/resources/static/js/OpenVidu.js index 1fecdab5..348395a1 100644 --- a/openvidu-browser/src/main/resources/static/js/OpenVidu.js +++ b/openvidu-browser/src/main/resources/static/js/OpenVidu.js @@ -188,7 +188,7 @@ function localstorage() { }).call(this,require('_process')) -},{"./debug":2,"_process":114}],2:[function(require,module,exports){ +},{"./debug":2,"_process":115}],2:[function(require,module,exports){ /** * This is the common logic for both the Node.js and web browser @@ -650,7 +650,7 @@ module.exports = function(stream, options) { return harker; } -},{"wildemitter":101}],7:[function(require,module,exports){ +},{"wildemitter":102}],7:[function(require,module,exports){ if (typeof Object.create === 'function') { // implementation from standard node.js 'util' module module.exports = function inherits(ctor, superCtor) { @@ -2181,7 +2181,7 @@ function WebSocketWithReconnection(config) { } module.exports = WebSocketWithReconnection; -},{"sockjs-client":33,"ws":103}],14:[function(require,module,exports){ +},{"sockjs-client":33,"ws":104}],14:[function(require,module,exports){ /* * (C) Copyright 2014 Kurento (http://kurento.org/) * @@ -3004,7 +3004,7 @@ RpcBuilder.clients = clients; RpcBuilder.clients.transports = transports; RpcBuilder.packers = packers; -},{"./Mapper":9,"./clients":10,"./clients/transports":12,"./packers":17,"events":113,"inherits":7}],15:[function(require,module,exports){ +},{"./Mapper":9,"./clients":10,"./clients/transports":12,"./packers":17,"events":114,"inherits":7}],15:[function(require,module,exports){ /** * JsonRPC 2.0 packer */ @@ -3910,7 +3910,7 @@ exports.WebRtcPeerSendonly = WebRtcPeerSendonly exports.WebRtcPeerSendrecv = WebRtcPeerSendrecv exports.hark = harkUtils -},{"events":113,"freeice":3,"hark":6,"inherits":7,"kurento-browser-extensions":undefined,"merge":20,"sdp-translator":29,"ua-parser-js":86,"uuid":90}],19:[function(require,module,exports){ +},{"events":114,"freeice":3,"hark":6,"inherits":7,"kurento-browser-extensions":undefined,"merge":20,"sdp-translator":29,"ua-parser-js":86,"uuid":90}],19:[function(require,module,exports){ /* * (C) Copyright 2014 Kurento (http://kurento.org/) * @@ -6854,7 +6854,7 @@ module.exports = function(SockJS, availableTransports) { }).call(this,require('_process')) -},{"./facade":39,"./info-iframe-receiver":42,"./location":45,"./utils/event":78,"./utils/iframe":79,"./utils/url":84,"_process":114,"debug":1,"json3":8}],41:[function(require,module,exports){ +},{"./facade":39,"./info-iframe-receiver":42,"./location":45,"./utils/event":78,"./utils/iframe":79,"./utils/url":84,"_process":115,"debug":1,"json3":8}],41:[function(require,module,exports){ (function (process){ 'use strict'; @@ -6908,7 +6908,7 @@ module.exports = InfoAjax; }).call(this,require('_process')) -},{"./utils/object":81,"_process":114,"debug":1,"events":35,"inherits":7,"json3":8}],42:[function(require,module,exports){ +},{"./utils/object":81,"_process":115,"debug":1,"events":35,"inherits":7,"json3":8}],42:[function(require,module,exports){ 'use strict'; var inherits = require('inherits') @@ -7017,7 +7017,7 @@ module.exports = InfoIframe; }).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) -},{"./info-iframe-receiver":42,"./transport/iframe":54,"./utils/event":78,"_process":114,"debug":1,"events":35,"inherits":7,"json3":8}],44:[function(require,module,exports){ +},{"./info-iframe-receiver":42,"./transport/iframe":54,"./utils/event":78,"_process":115,"debug":1,"events":35,"inherits":7,"json3":8}],44:[function(require,module,exports){ (function (process){ 'use strict'; @@ -7111,7 +7111,7 @@ module.exports = InfoReceiver; }).call(this,require('_process')) -},{"./info-ajax":41,"./info-iframe":43,"./transport/sender/xdr":66,"./transport/sender/xhr-cors":67,"./transport/sender/xhr-fake":68,"./transport/sender/xhr-local":69,"./utils/url":84,"_process":114,"debug":1,"events":35,"inherits":7}],45:[function(require,module,exports){ +},{"./info-ajax":41,"./info-iframe":43,"./transport/sender/xdr":66,"./transport/sender/xhr-cors":67,"./transport/sender/xhr-fake":68,"./transport/sender/xhr-local":69,"./utils/url":84,"_process":115,"debug":1,"events":35,"inherits":7}],45:[function(require,module,exports){ (function (global){ 'use strict'; @@ -7512,7 +7512,7 @@ module.exports = function(availableTransports) { }).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) -},{"./event/close":34,"./event/event":36,"./event/eventtarget":37,"./event/trans-message":38,"./iframe-bootstrap":40,"./info-receiver":44,"./location":45,"./shims":47,"./utils/browser":76,"./utils/escape":77,"./utils/event":78,"./utils/log":80,"./utils/object":81,"./utils/random":82,"./utils/transport":83,"./utils/url":84,"./version":85,"_process":114,"debug":1,"inherits":7,"json3":8,"url-parse":87}],47:[function(require,module,exports){ +},{"./event/close":34,"./event/event":36,"./event/eventtarget":37,"./event/trans-message":38,"./iframe-bootstrap":40,"./info-receiver":44,"./location":45,"./shims":47,"./utils/browser":76,"./utils/escape":77,"./utils/event":78,"./utils/log":80,"./utils/object":81,"./utils/random":82,"./utils/transport":83,"./utils/url":84,"./version":85,"_process":115,"debug":1,"inherits":7,"json3":8,"url-parse":87}],47:[function(require,module,exports){ /* eslint-disable */ /* jscs: disable */ 'use strict'; @@ -8205,7 +8205,7 @@ module.exports = AbstractXHRObject; }).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) -},{"../../utils/event":78,"../../utils/url":84,"_process":114,"debug":1,"events":35,"inherits":7}],50:[function(require,module,exports){ +},{"../../utils/event":78,"../../utils/url":84,"_process":115,"debug":1,"events":35,"inherits":7}],50:[function(require,module,exports){ (function (global){ module.exports = global.EventSource; @@ -8426,7 +8426,7 @@ module.exports = IframeTransport; }).call(this,require('_process')) -},{"../utils/event":78,"../utils/iframe":79,"../utils/random":82,"../utils/url":84,"../version":85,"_process":114,"debug":1,"events":35,"inherits":7,"json3":8}],55:[function(require,module,exports){ +},{"../utils/event":78,"../utils/iframe":79,"../utils/random":82,"../utils/url":84,"../version":85,"_process":115,"debug":1,"events":35,"inherits":7,"json3":8}],55:[function(require,module,exports){ (function (global){ 'use strict'; @@ -8519,7 +8519,7 @@ module.exports = AjaxBasedTransport; }).call(this,require('_process')) -},{"../../utils/url":84,"./sender-receiver":60,"_process":114,"debug":1,"inherits":7}],57:[function(require,module,exports){ +},{"../../utils/url":84,"./sender-receiver":60,"_process":115,"debug":1,"inherits":7}],57:[function(require,module,exports){ (function (process){ 'use strict'; @@ -8611,7 +8611,7 @@ module.exports = BufferedSender; }).call(this,require('_process')) -},{"_process":114,"debug":1,"events":35,"inherits":7}],58:[function(require,module,exports){ +},{"_process":115,"debug":1,"events":35,"inherits":7}],58:[function(require,module,exports){ (function (global){ 'use strict'; @@ -8711,7 +8711,7 @@ module.exports = Polling; }).call(this,require('_process')) -},{"_process":114,"debug":1,"events":35,"inherits":7}],60:[function(require,module,exports){ +},{"_process":115,"debug":1,"events":35,"inherits":7}],60:[function(require,module,exports){ (function (process){ 'use strict'; @@ -8761,7 +8761,7 @@ module.exports = SenderReceiver; }).call(this,require('_process')) -},{"../../utils/url":84,"./buffered-sender":57,"./polling":59,"_process":114,"debug":1,"inherits":7}],61:[function(require,module,exports){ +},{"../../utils/url":84,"./buffered-sender":57,"./polling":59,"_process":115,"debug":1,"inherits":7}],61:[function(require,module,exports){ (function (process){ 'use strict'; @@ -8829,7 +8829,7 @@ module.exports = EventSourceReceiver; }).call(this,require('_process')) -},{"_process":114,"debug":1,"events":35,"eventsource":50,"inherits":7}],62:[function(require,module,exports){ +},{"_process":115,"debug":1,"events":35,"eventsource":50,"inherits":7}],62:[function(require,module,exports){ (function (process,global){ 'use strict'; @@ -8921,7 +8921,7 @@ module.exports = HtmlfileReceiver; }).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) -},{"../../utils/iframe":79,"../../utils/random":82,"../../utils/url":84,"_process":114,"debug":1,"events":35,"inherits":7}],63:[function(require,module,exports){ +},{"../../utils/iframe":79,"../../utils/random":82,"../../utils/url":84,"_process":115,"debug":1,"events":35,"inherits":7}],63:[function(require,module,exports){ (function (process,global){ 'use strict'; @@ -9109,7 +9109,7 @@ module.exports = JsonpReceiver; }).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) -},{"../../utils/browser":76,"../../utils/iframe":79,"../../utils/random":82,"../../utils/url":84,"_process":114,"debug":1,"events":35,"inherits":7}],64:[function(require,module,exports){ +},{"../../utils/browser":76,"../../utils/iframe":79,"../../utils/random":82,"../../utils/url":84,"_process":115,"debug":1,"events":35,"inherits":7}],64:[function(require,module,exports){ (function (process){ 'use strict'; @@ -9184,7 +9184,7 @@ module.exports = XhrReceiver; }).call(this,require('_process')) -},{"_process":114,"debug":1,"events":35,"inherits":7}],65:[function(require,module,exports){ +},{"_process":115,"debug":1,"events":35,"inherits":7}],65:[function(require,module,exports){ (function (process,global){ 'use strict'; @@ -9288,7 +9288,7 @@ module.exports = function(url, payload, callback) { }).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) -},{"../../utils/random":82,"../../utils/url":84,"_process":114,"debug":1}],66:[function(require,module,exports){ +},{"../../utils/random":82,"../../utils/url":84,"_process":115,"debug":1}],66:[function(require,module,exports){ (function (process,global){ 'use strict'; @@ -9396,7 +9396,7 @@ module.exports = XDRObject; }).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) -},{"../../utils/browser":76,"../../utils/event":78,"../../utils/url":84,"_process":114,"debug":1,"events":35,"inherits":7}],67:[function(require,module,exports){ +},{"../../utils/browser":76,"../../utils/event":78,"../../utils/url":84,"_process":115,"debug":1,"events":35,"inherits":7}],67:[function(require,module,exports){ 'use strict'; var inherits = require('inherits') @@ -9561,7 +9561,7 @@ module.exports = WebSocketTransport; }).call(this,require('_process')) -},{"../utils/event":78,"../utils/url":84,"./driver/websocket":51,"_process":114,"debug":1,"events":35,"inherits":7}],71:[function(require,module,exports){ +},{"../utils/event":78,"../utils/url":84,"./driver/websocket":51,"_process":115,"debug":1,"events":35,"inherits":7}],71:[function(require,module,exports){ 'use strict'; var inherits = require('inherits') @@ -10076,7 +10076,7 @@ if (global.document) { }).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) -},{"./browser":76,"./event":78,"_process":114,"debug":1,"json3":8}],80:[function(require,module,exports){ +},{"./browser":76,"./event":78,"_process":115,"debug":1,"json3":8}],80:[function(require,module,exports){ (function (global){ 'use strict'; @@ -10211,7 +10211,7 @@ module.exports = function(availableTransports) { }).call(this,require('_process')) -},{"_process":114,"debug":1}],84:[function(require,module,exports){ +},{"_process":115,"debug":1}],84:[function(require,module,exports){ (function (process){ 'use strict'; @@ -10263,7 +10263,7 @@ module.exports = { }).call(this,require('_process')) -},{"_process":114,"debug":1,"url-parse":87}],85:[function(require,module,exports){ +},{"_process":115,"debug":1,"url-parse":87}],85:[function(require,module,exports){ module.exports = '1.1.1'; },{}],86:[function(require,module,exports){ @@ -11892,6 +11892,24 @@ uuid.unparse = unparse; module.exports = uuid; },{"./rng":89}],91:[function(require,module,exports){ +(function (global){ +/* + * 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 adapterFactory = require('./adapter_factory.js'); +module.exports = adapterFactory({window: global.window}); + +}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) + +},{"./adapter_factory.js":92}],92:[function(require,module,exports){ /* * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. * @@ -11904,15 +11922,20 @@ module.exports = uuid; 'use strict'; // Shimming starts here. -(function() { +module.exports = function(dependencies) { + var window = dependencies && dependencies.window; + // Utils. var utils = require('./utils'); var logging = utils.log; - var browserDetails = utils.browserDetails; + var browserDetails = utils.detectBrowser(window); + // Export to the adapter global object visible in the browser. - module.exports.browserDetails = browserDetails; - module.exports.extractVersion = utils.extractVersion; - module.exports.disableLog = utils.disableLog; + var adapter = { + browserDetails: browserDetails, + extractVersion: utils.extractVersion, + disableLog: utils.disableLog + }; // Uncomment 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 @@ -11931,67 +11954,72 @@ module.exports = uuid; case 'chrome': if (!chromeShim || !chromeShim.shimPeerConnection) { logging('Chrome shim is not included in this adapter release.'); - return; + return adapter; } logging('adapter.js shimming chrome.'); // Export to the adapter global object visible in the browser. - module.exports.browserShim = chromeShim; + adapter.browserShim = chromeShim; - chromeShim.shimGetUserMedia(); - chromeShim.shimMediaStream(); - utils.shimCreateObjectURL(); - chromeShim.shimSourceObject(); - chromeShim.shimPeerConnection(); - chromeShim.shimOnTrack(); - chromeShim.shimGetSendersWithDtmf(); + chromeShim.shimGetUserMedia(window); + chromeShim.shimMediaStream(window); + utils.shimCreateObjectURL(window); + chromeShim.shimSourceObject(window); + chromeShim.shimPeerConnection(window); + chromeShim.shimOnTrack(window); + chromeShim.shimGetSendersWithDtmf(window); break; case 'firefox': if (!firefoxShim || !firefoxShim.shimPeerConnection) { logging('Firefox shim is not included in this adapter release.'); - return; + return adapter; } logging('adapter.js shimming firefox.'); // Export to the adapter global object visible in the browser. - module.exports.browserShim = firefoxShim; + adapter.browserShim = firefoxShim; - firefoxShim.shimGetUserMedia(); - utils.shimCreateObjectURL(); - firefoxShim.shimSourceObject(); - firefoxShim.shimPeerConnection(); - firefoxShim.shimOnTrack(); + firefoxShim.shimGetUserMedia(window); + utils.shimCreateObjectURL(window); + firefoxShim.shimSourceObject(window); + firefoxShim.shimPeerConnection(window); + firefoxShim.shimOnTrack(window); break; case 'edge': if (!edgeShim || !edgeShim.shimPeerConnection) { logging('MS edge shim is not included in this adapter release.'); - return; + return adapter; } logging('adapter.js shimming edge.'); // Export to the adapter global object visible in the browser. - module.exports.browserShim = edgeShim; + adapter.browserShim = edgeShim; - edgeShim.shimGetUserMedia(); - utils.shimCreateObjectURL(); - edgeShim.shimPeerConnection(); - edgeShim.shimReplaceTrack(); + edgeShim.shimGetUserMedia(window); + utils.shimCreateObjectURL(window); + edgeShim.shimPeerConnection(window); + edgeShim.shimReplaceTrack(window); break; case 'safari': if (!safariShim) { logging('Safari shim is not included in this adapter release.'); - return; + return adapter; } logging('adapter.js shimming safari.'); // Export to the adapter global object visible in the browser. - module.exports.browserShim = safariShim; + adapter.browserShim = safariShim; - safariShim.shimOnAddStream(); - safariShim.shimGetUserMedia(); + safariShim.shimCallbacksAPI(window); + safariShim.shimAddStream(window); + safariShim.shimOnAddStream(window); + safariShim.shimGetUserMedia(window); break; default: logging('Unsupported browser!'); + break; } -})(); -},{"./chrome/chrome_shim":92,"./edge/edge_shim":94,"./firefox/firefox_shim":97,"./safari/safari_shim":99,"./utils":100}],92:[function(require,module,exports){ + return adapter; +}; + +},{"./chrome/chrome_shim":93,"./edge/edge_shim":95,"./firefox/firefox_shim":98,"./safari/safari_shim":100,"./utils":101}],93:[function(require,module,exports){ /* * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. @@ -12002,15 +12030,15 @@ module.exports = uuid; */ /* eslint-env node */ 'use strict'; -var logging = require('../utils.js').log; -var browserDetails = require('../utils.js').browserDetails; +var utils = require('../utils.js'); +var logging = utils.log; var chromeShim = { - shimMediaStream: function() { + shimMediaStream: function(window) { window.MediaStream = window.MediaStream || window.webkitMediaStream; }, - shimOnTrack: function() { + shimOnTrack: function(window) { if (typeof window === 'object' && window.RTCPeerConnection && !('ontrack' in window.RTCPeerConnection.prototype)) { Object.defineProperty(window.RTCPeerConnection.prototype, 'ontrack', { @@ -12028,16 +12056,33 @@ var chromeShim = { // 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) { + var receiver; + if (window.RTCPeerConnection.prototype.getReceivers) { + receiver = self.getReceivers().find(function(r) { + return r.track.id === te.track.id; + }); + } else { + receiver = {track: te.track}; + } + var event = new Event('track'); event.track = te.track; - event.receiver = {track: te.track}; + event.receiver = receiver; event.streams = [e.stream]; self.dispatchEvent(event); }); e.stream.getTracks().forEach(function(track) { + var receiver; + if (window.RTCPeerConnection.prototype.getReceivers) { + receiver = self.getReceivers().find(function(r) { + return r.track.id === track.id; + }); + } else { + receiver = {track: track}; + } var event = new Event('track'); event.track = track; - event.receiver = {track: track}; + event.receiver = receiver; event.streams = [e.stream]; this.dispatchEvent(event); }.bind(this)); @@ -12047,17 +12092,76 @@ var chromeShim = { } }, - shimGetSendersWithDtmf: function() { + shimGetSendersWithDtmf: function(window) { if (typeof window === 'object' && window.RTCPeerConnection && - !('getSenders' in RTCPeerConnection.prototype) && - 'createDTMFSender' in RTCPeerConnection.prototype) { - RTCPeerConnection.prototype.getSenders = function() { - return this._senders; + !('getSenders' in window.RTCPeerConnection.prototype) && + 'createDTMFSender' in window.RTCPeerConnection.prototype) { + window.RTCPeerConnection.prototype.getSenders = function() { + return this._senders || []; }; - var origAddStream = RTCPeerConnection.prototype.addStream; - var origRemoveStream = RTCPeerConnection.prototype.removeStream; + var origAddStream = window.RTCPeerConnection.prototype.addStream; + var origRemoveStream = window.RTCPeerConnection.prototype.removeStream; - RTCPeerConnection.prototype.addStream = function(stream) { + if (!window.RTCPeerConnection.prototype.addTrack) { + window.RTCPeerConnection.prototype.addTrack = function(track, stream) { + var pc = this; + if (pc.signalingState === 'closed') { + throw new DOMException( + 'The RTCPeerConnection\'s signalingState is \'closed\'.', + 'InvalidStateError'); + } + var streams = [].slice.call(arguments, 1); + if (streams.length !== 1 || + !streams[0].getTracks().find(function(t) { + return t === track; + })) { + // this is not fully correct but all we can manage without + // [[associated MediaStreams]] internal slot. + throw new DOMException( + 'The adapter.js addTrack polyfill only supports a single ' + + ' stream which is associated with the specified track.', + 'NotSupportedError'); + } + + pc._senders = pc._senders || []; + var alreadyExists = pc._senders.find(function(t) { + return t.track === track; + }); + if (alreadyExists) { + throw new DOMException('Track already exists.', + 'InvalidAccessError'); + } + + pc._streams = pc._streams || {}; + var oldStream = pc._streams[stream.id]; + if (oldStream) { + oldStream.addTrack(track); + pc.removeStream(oldStream); + pc.addStream(oldStream); + } else { + var newStream = new window.MediaStream([track]); + pc._streams[stream.id] = newStream; + pc.addStream(newStream); + } + + var sender = { + track: track, + get dtmf() { + if (this._dtmf === undefined) { + if (track.kind === 'audio') { + this._dtmf = pc.createDTMFSender(track); + } else { + this._dtmf = null; + } + } + return this._dtmf; + } + }; + pc._senders.push(sender); + return sender; + }; + } + window.RTCPeerConnection.prototype.addStream = function(stream) { var pc = this; pc._senders = pc._senders || []; origAddStream.apply(pc, [stream]); @@ -12078,7 +12182,7 @@ var chromeShim = { }); }; - RTCPeerConnection.prototype.removeStream = function(stream) { + window.RTCPeerConnection.prototype.removeStream = function(stream) { var pc = this; pc._senders = pc._senders || []; origRemoveStream.apply(pc, [stream]); @@ -12094,7 +12198,9 @@ var chromeShim = { } }, - shimSourceObject: function() { + shimSourceObject: function(window) { + var URL = window && window.URL; + if (typeof window === 'object') { if (window.HTMLMediaElement && !('srcObject' in window.HTMLMediaElement.prototype)) { @@ -12136,7 +12242,9 @@ var chromeShim = { } }, - shimPeerConnection: function() { + shimPeerConnection: function(window) { + var browserDetails = utils.detectBrowser(window); + // The RTCPeerConnection object. if (!window.RTCPeerConnection) { window.RTCPeerConnection = function(pcConfig, pcConstraints) { @@ -12148,20 +12256,21 @@ var chromeShim = { pcConfig.iceTransports = pcConfig.iceTransportPolicy; } - return new webkitRTCPeerConnection(pcConfig, pcConstraints); + return new window.webkitRTCPeerConnection(pcConfig, pcConstraints); }; - window.RTCPeerConnection.prototype = webkitRTCPeerConnection.prototype; + window.RTCPeerConnection.prototype = + window.webkitRTCPeerConnection.prototype; // wrap static methods. Currently just generateCertificate. - if (webkitRTCPeerConnection.generateCertificate) { + if (window.webkitRTCPeerConnection.generateCertificate) { Object.defineProperty(window.RTCPeerConnection, 'generateCertificate', { get: function() { - return webkitRTCPeerConnection.generateCertificate; + return window.webkitRTCPeerConnection.generateCertificate; } }); } } else { // migrate from non-spec RTCIceServer.url to RTCIceServer.urls - var OrigPeerConnection = RTCPeerConnection; + var OrigPeerConnection = window.RTCPeerConnection; window.RTCPeerConnection = function(pcConfig, pcConstraints) { if (pcConfig && pcConfig.iceServers) { var newIceServers = []; @@ -12190,8 +12299,8 @@ var chromeShim = { }); } - var origGetStats = RTCPeerConnection.prototype.getStats; - RTCPeerConnection.prototype.getStats = function(selector, + var origGetStats = window.RTCPeerConnection.prototype.getStats; + window.RTCPeerConnection.prototype.getStats = function(selector, successCallback, errorCallback) { var self = this; var args = arguments; @@ -12233,7 +12342,7 @@ var chromeShim = { // shim getStats with maplike support var makeMapStats = function(stats) { return new Map(Object.keys(stats).map(function(key) { - return[key, stats[key]]; + return [key, stats[key]]; })); }; @@ -12243,7 +12352,7 @@ var chromeShim = { }; return origGetStats.apply(this, [successCallbackWrapper_, - arguments[0]]); + arguments[0]]); } // promise-support @@ -12259,8 +12368,8 @@ var chromeShim = { if (browserDetails.version < 51) { ['setLocalDescription', 'setRemoteDescription', 'addIceCandidate'] .forEach(function(method) { - var nativeMethod = RTCPeerConnection.prototype[method]; - RTCPeerConnection.prototype[method] = function() { + var nativeMethod = window.RTCPeerConnection.prototype[method]; + window.RTCPeerConnection.prototype[method] = function() { var args = arguments; var self = this; var promise = new Promise(function(resolve, reject) { @@ -12285,8 +12394,8 @@ var chromeShim = { // bugs) since M52: crbug/619289 if (browserDetails.version < 52) { ['createOffer', 'createAnswer'].forEach(function(method) { - var nativeMethod = RTCPeerConnection.prototype[method]; - RTCPeerConnection.prototype[method] = function() { + var nativeMethod = window.RTCPeerConnection.prototype[method]; + window.RTCPeerConnection.prototype[method] = function() { var self = this; if (arguments.length < 1 || (arguments.length === 1 && typeof arguments[0] === 'object')) { @@ -12303,18 +12412,19 @@ var chromeShim = { // shim implicit creation of RTCSessionDescription/RTCIceCandidate ['setLocalDescription', 'setRemoteDescription', 'addIceCandidate'] .forEach(function(method) { - var nativeMethod = RTCPeerConnection.prototype[method]; - RTCPeerConnection.prototype[method] = function() { + var nativeMethod = window.RTCPeerConnection.prototype[method]; + window.RTCPeerConnection.prototype[method] = function() { arguments[0] = new ((method === 'addIceCandidate') ? - RTCIceCandidate : RTCSessionDescription)(arguments[0]); + window.RTCIceCandidate : + window.RTCSessionDescription)(arguments[0]); return nativeMethod.apply(this, arguments); }; }); // support for addIceCandidate(null or undefined) var nativeAddIceCandidate = - RTCPeerConnection.prototype.addIceCandidate; - RTCPeerConnection.prototype.addIceCandidate = function() { + window.RTCPeerConnection.prototype.addIceCandidate; + window.RTCPeerConnection.prototype.addIceCandidate = function() { if (!arguments[0]) { if (arguments[1]) { arguments[1].apply(null); @@ -12337,7 +12447,7 @@ module.exports = { shimGetUserMedia: require('./getusermedia') }; -},{"../utils.js":100,"./getusermedia":93}],93:[function(require,module,exports){ +},{"../utils.js":101,"./getusermedia":94}],94:[function(require,module,exports){ /* * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. * @@ -12347,11 +12457,14 @@ module.exports = { */ /* eslint-env node */ 'use strict'; -var logging = require('../utils.js').log; -var browserDetails = require('../utils.js').browserDetails; +var utils = require('../utils.js'); +var logging = utils.log; // Expose public methods. -module.exports = function() { +module.exports = function(window) { + var browserDetails = utils.detectBrowser(window); + var navigator = window && window.navigator; + var constraintsToChrome_ = function(c) { if (typeof c !== 'object' || c.mandatory || c.optional) { return c; @@ -12405,7 +12518,16 @@ module.exports = function() { var shimConstraints_ = function(constraints, func) { constraints = JSON.parse(JSON.stringify(constraints)); - if (constraints && constraints.audio) { + if (constraints && typeof constraints.audio === 'object') { + var remap = function(obj, a, b) { + if (a in obj && !(b in obj)) { + obj[b] = obj[a]; + delete obj[a]; + } + }; + constraints = JSON.parse(JSON.stringify(constraints)); + remap(constraints.audio, 'autoGainControl', 'googAutoGainControl'); + remap(constraints.audio, 'noiseSuppression', 'googNoiseSuppression'); constraints.audio = constraintsToChrome_(constraints.audio); } if (constraints && typeof constraints.video === 'object') { @@ -12461,7 +12583,12 @@ module.exports = function() { return { name: { PermissionDeniedError: 'NotAllowedError', - ConstraintNotSatisfiedError: 'OverconstrainedError' + InvalidStateError: 'NotReadableError', + DevicesNotFoundError: 'NotFoundError', + ConstraintNotSatisfiedError: 'OverconstrainedError', + TrackStartError: 'NotReadableError', + MediaDeviceFailedDueToShutdown: 'NotReadableError', + MediaDeviceKillSwitchOn: 'NotReadableError' }[e.name] || e.name, message: e.message, constraint: e.constraintName, @@ -12494,12 +12621,12 @@ module.exports = function() { enumerateDevices: function() { return new Promise(function(resolve) { var kinds = {audio: 'audioinput', video: 'videoinput'}; - return MediaStreamTrack.getSources(function(devices) { + return window.MediaStreamTrack.getSources(function(devices) { resolve(devices.map(function(device) { return {label: device.label, - kind: kinds[device.kind], - deviceId: device.id, - groupId: ''}; + kind: kinds[device.kind], + deviceId: device.id, + groupId: ''}; })); }); }); @@ -12557,7 +12684,7 @@ module.exports = function() { } }; -},{"../utils.js":100}],94:[function(require,module,exports){ +},{"../utils.js":101}],95:[function(require,module,exports){ /* * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. * @@ -12568,12 +12695,14 @@ module.exports = function() { /* eslint-env node */ 'use strict'; -var browserDetails = require('../utils').browserDetails; +var utils = require('../utils'); var shimRTCPeerConnection = require('./rtcpeerconnection_shim'); module.exports = { shimGetUserMedia: require('./getusermedia'), - shimPeerConnection: function() { + shimPeerConnection: function(window) { + var browserDetails = utils.detectBrowser(window); + if (window.RTCIceGatherer) { // ORTC defines an RTCIceCandidate object but no constructor. // Not implemented in Edge. @@ -12595,8 +12724,8 @@ module.exports = { // addStream, see below. No longer required in 15025+ if (browserDetails.version < 15025) { var origMSTEnabled = Object.getOwnPropertyDescriptor( - MediaStreamTrack.prototype, 'enabled'); - Object.defineProperty(MediaStreamTrack.prototype, 'enabled', { + window.MediaStreamTrack.prototype, 'enabled'); + Object.defineProperty(window.MediaStreamTrack.prototype, 'enabled', { set: function(value) { origMSTEnabled.set.call(this, value); var ev = new Event('enabled'); @@ -12606,17 +12735,20 @@ module.exports = { }); } } - window.RTCPeerConnection = shimRTCPeerConnection(browserDetails.version); + window.RTCPeerConnection = + shimRTCPeerConnection(window, browserDetails.version); }, - shimReplaceTrack: function() { + shimReplaceTrack: function(window) { // ORTC has replaceTrack -- https://github.com/w3c/ortc/issues/614 - if (window.RTCRtpSender && !('replaceTrack' in RTCRtpSender.prototype)) { - RTCRtpSender.prototype.replaceTrack = RTCRtpSender.prototype.setTrack; + if (window.RTCRtpSender && + !('replaceTrack' in window.RTCRtpSender.prototype)) { + window.RTCRtpSender.prototype.replaceTrack = + window.RTCRtpSender.prototype.setTrack; } } }; -},{"../utils":100,"./getusermedia":95,"./rtcpeerconnection_shim":96}],95:[function(require,module,exports){ +},{"../utils":101,"./getusermedia":96,"./rtcpeerconnection_shim":97}],96:[function(require,module,exports){ /* * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. * @@ -12628,7 +12760,9 @@ module.exports = { 'use strict'; // Expose public methods. -module.exports = function() { +module.exports = function(window) { + var navigator = window && window.navigator; + var shimError_ = function(e) { return { name: {PermissionDeniedError: 'NotAllowedError'}[e.name] || e.name, @@ -12650,7 +12784,7 @@ module.exports = function() { }; }; -},{}],96:[function(require,module,exports){ +},{}],97:[function(require,module,exports){ /* * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. * @@ -12723,7 +12857,99 @@ function filterIceServers(iceServers, edgeVersion) { }); } -module.exports = function(edgeVersion) { +// Determines the intersection of local and remote capabilities. +function getCommonCapabilities(localCapabilities, remoteCapabilities) { + var commonCapabilities = { + codecs: [], + headerExtensions: [], + fecMechanisms: [] + }; + + var findCodecByPayloadType = function(pt, codecs) { + pt = parseInt(pt, 10); + for (var i = 0; i < codecs.length; i++) { + if (codecs[i].payloadType === pt || + codecs[i].preferredPayloadType === pt) { + return codecs[i]; + } + } + }; + + var rtxCapabilityMatches = function(lRtx, rRtx, lCodecs, rCodecs) { + var lCodec = findCodecByPayloadType(lRtx.parameters.apt, lCodecs); + var rCodec = findCodecByPayloadType(rRtx.parameters.apt, rCodecs); + return lCodec && rCodec && + lCodec.name.toLowerCase() === rCodec.name.toLowerCase(); + }; + + 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) { + if (lCodec.name.toLowerCase() === 'rtx' && + lCodec.parameters && rCodec.parameters.apt) { + // for RTX we need to find the local rtx that has a apt + // which points to the same local codec as the remote one. + if (!rtxCapabilityMatches(lCodec, rCodec, + localCapabilities.codecs, remoteCapabilities.codecs)) { + continue; + } + } + rCodec = JSON.parse(JSON.stringify(rCodec)); // deepcopy + // number of channels is the highest common number of channels + rCodec.numChannels = Math.min(lCodec.numChannels, + rCodec.numChannels); + // push rCodec so we reply with offerer payload type + commonCapabilities.codecs.push(rCodec); + + // determine common feedback mechanisms + rCodec.rtcpFeedback = rCodec.rtcpFeedback.filter(function(fb) { + for (var j = 0; j < lCodec.rtcpFeedback.length; j++) { + if (lCodec.rtcpFeedback[j].type === fb.type && + lCodec.rtcpFeedback[j].parameter === fb.parameter) { + return true; + } + } + return false; + }); + // FIXME: also need to determine .parameters + // see https://github.com/openpeer/ortc/issues/569 + 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; +} + +// is action=setLocalDescription with type allowed in signalingState +function isActionAllowedInSignalingState(action, type, signalingState) { + return { + offer: { + setLocalDescription: ['stable', 'have-local-offer'], + setRemoteDescription: ['stable', 'have-remote-offer'] + }, + answer: { + setLocalDescription: ['have-remote-offer', 'have-local-pranswer'], + setRemoteDescription: ['have-local-offer', 'have-remote-pranswer'] + } + }[type][action].indexOf(signalingState) !== -1; +} + +module.exports = function(window, edgeVersion) { var RTCPeerConnection = function(config) { var self = this; @@ -12733,6 +12959,8 @@ module.exports = function(edgeVersion) { self[method] = _eventTarget[method].bind(_eventTarget); }); + this.needNegotiation = false; + this.onicecandidate = null; this.onaddstream = null; this.ontrack = null; @@ -12753,11 +12981,11 @@ module.exports = function(edgeVersion) { return self.remoteStreams; }; - this.localDescription = new RTCSessionDescription({ + this.localDescription = new window.RTCSessionDescription({ type: '', sdp: '' }); - this.remoteDescription = new RTCSessionDescription({ + this.remoteDescription = new window.RTCSessionDescription({ type: '', sdp: '' }); @@ -12846,9 +13074,66 @@ module.exports = function(edgeVersion) { return this._config; }; + // internal helper to create a transceiver object. + // (whih is not yet the same as the WebRTC 1.0 transceiver) + RTCPeerConnection.prototype._createTransceiver = function(kind) { + var hasBundleTransport = this.transceivers.length > 0; + var transceiver = { + track: null, + iceGatherer: null, + iceTransport: null, + dtlsTransport: null, + localCapabilities: null, + remoteCapabilities: null, + rtpSender: null, + rtpReceiver: null, + kind: kind, + mid: null, + sendEncodingParameters: null, + recvEncodingParameters: null, + stream: null, + wantReceive: true + }; + if (this.usingBundle && hasBundleTransport) { + transceiver.iceTransport = this.transceivers[0].iceTransport; + transceiver.dtlsTransport = this.transceivers[0].dtlsTransport; + } else { + var transports = this._createIceAndDtlsTransports(); + transceiver.iceTransport = transports.iceTransport; + transceiver.dtlsTransport = transports.dtlsTransport; + } + this.transceivers.push(transceiver); + return transceiver; + }; + + RTCPeerConnection.prototype.addTrack = function(track, stream) { + var transceiver; + for (var i = 0; i < this.transceivers.length; i++) { + if (!this.transceivers[i].track && + this.transceivers[i].kind === track.kind) { + transceiver = this.transceivers[i]; + } + } + if (!transceiver) { + transceiver = this._createTransceiver(track.kind); + } + + transceiver.track = track; + transceiver.stream = stream; + transceiver.rtpSender = new window.RTCRtpSender(track, + transceiver.dtlsTransport); + + this._maybeFireNegotiationNeeded(); + return transceiver.rtpSender; + }; + RTCPeerConnection.prototype.addStream = function(stream) { + var self = this; if (edgeVersion >= 15025) { this.localStreams.push(stream); + stream.getTracks().forEach(function(track) { + self.addTrack(track, stream); + }); } else { // Clone is necessary for local demos mostly, attaching directly // to two different senders does not work (build 10547). @@ -12860,6 +13145,9 @@ module.exports = function(edgeVersion) { clonedTrack.enabled = event.enabled; }); }); + clonedStream.getTracks().forEach(function(track) { + self.addTrack(track, clonedStream); + }); this.localStreams.push(clonedStream); } this._maybeFireNegotiationNeeded(); @@ -12891,92 +13179,11 @@ module.exports = function(edgeVersion) { }); }; - // Determines the intersection of local and remote capabilities. - RTCPeerConnection.prototype._getCommonCapabilities = function( - localCapabilities, remoteCapabilities) { - var commonCapabilities = { - codecs: [], - headerExtensions: [], - fecMechanisms: [] - }; - - var findCodecByPayloadType = function(pt, codecs) { - pt = parseInt(pt, 10); - for (var i = 0; i < codecs.length; i++) { - if (codecs[i].payloadType === pt || - codecs[i].preferredPayloadType === pt) { - return codecs[i]; - } - } - }; - - var rtxCapabilityMatches = function(lRtx, rRtx, lCodecs, rCodecs) { - var lCodec = findCodecByPayloadType(lRtx.parameters.apt, lCodecs); - var rCodec = findCodecByPayloadType(rRtx.parameters.apt, rCodecs); - return lCodec && rCodec && - lCodec.name.toLowerCase() === rCodec.name.toLowerCase(); - }; - - 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) { - if (lCodec.name.toLowerCase() === 'rtx' && - lCodec.parameters && rCodec.parameters.apt) { - // for RTX we need to find the local rtx that has a apt - // which points to the same local codec as the remote one. - if (!rtxCapabilityMatches(lCodec, rCodec, - localCapabilities.codecs, remoteCapabilities.codecs)) { - continue; - } - } - rCodec = JSON.parse(JSON.stringify(rCodec)); // deepcopy - // number of channels is the highest common number of channels - rCodec.numChannels = Math.min(lCodec.numChannels, - rCodec.numChannels); - // push rCodec so we reply with offerer payload type - commonCapabilities.codecs.push(rCodec); - - // determine common feedback mechanisms - rCodec.rtcpFeedback = rCodec.rtcpFeedback.filter(function(fb) { - for (var j = 0; j < lCodec.rtcpFeedback.length; j++) { - if (lCodec.rtcpFeedback[j].type === fb.type && - lCodec.rtcpFeedback[j].parameter === fb.parameter) { - return true; - } - } - return false; - }); - // FIXME: also need to determine .parameters - // see https://github.com/openpeer/ortc/issues/569 - 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; - }; - - // Create ICE gatherer, ICE transport and DTLS transport. - RTCPeerConnection.prototype._createIceAndDtlsTransports = function(mid, + // Create ICE gatherer and hook it up. + RTCPeerConnection.prototype._createIceGatherer = function(mid, sdpMLineIndex) { var self = this; - var iceGatherer = new RTCIceGatherer(self.iceOptions); - var iceTransport = new RTCIceTransport(iceGatherer); + var iceGatherer = new window.RTCIceGatherer(self.iceOptions); iceGatherer.onlocalcandidate = function(evt) { var event = new Event('icecandidate'); event.candidate = {sdpMid: mid, sdpMLineIndex: sdpMLineIndex}; @@ -12992,7 +13199,7 @@ module.exports = function(edgeVersion) { } } else { // RTCIceCandidate doesn't have a component, needs to be added - cand.component = iceTransport.component === 'RTCP' ? 2 : 1; + cand.component = 1; event.candidate.candidate = SDPUtils.writeCandidate(cand); } @@ -13049,22 +13256,29 @@ module.exports = function(edgeVersion) { break; } }; + return iceGatherer; + }; + + // Create ICE transport and DTLS transport. + RTCPeerConnection.prototype._createIceAndDtlsTransports = function() { + var self = this; + var iceTransport = new window.RTCIceTransport(null); iceTransport.onicestatechange = function() { self._updateConnectionState(); }; - var dtlsTransport = new RTCDtlsTransport(iceTransport); + var dtlsTransport = new window.RTCDtlsTransport(iceTransport); dtlsTransport.ondtlsstatechange = function() { self._updateConnectionState(); }; dtlsTransport.onerror = function() { // onerror does not set state to failed by itself. - dtlsTransport.state = 'failed'; + Object.defineProperty(dtlsTransport, 'state', + {value: 'failed', writable: true}); self._updateConnectionState(); }; return { - iceGatherer: iceGatherer, iceTransport: iceTransport, dtlsTransport: dtlsTransport }; @@ -13095,7 +13309,7 @@ module.exports = function(edgeVersion) { // Start the RTP Sender and Receiver for a transceiver. RTCPeerConnection.prototype._transceive = function(transceiver, send, recv) { - var params = this._getCommonCapabilities(transceiver.localCapabilities, + var params = getCommonCapabilities(transceiver.localCapabilities, transceiver.remoteCapabilities); if (send && transceiver.rtpSender) { params.encodings = transceiver.sendEncodingParameters; @@ -13131,6 +13345,18 @@ module.exports = function(edgeVersion) { RTCPeerConnection.prototype.setLocalDescription = function(description) { var self = this; + + if (!isActionAllowedInSignalingState('setLocalDescription', + description.type, this.signalingState)) { + var e = new Error('Can not set local ' + description.type + + ' in state ' + this.signalingState); + e.name = 'InvalidStateError'; + if (arguments.length > 2 && typeof arguments[2] === 'function') { + window.setTimeout(arguments[2], 0, e); + } + return Promise.reject(e); + } + var sections; var sessionpart; if (description.type === 'offer') { @@ -13162,8 +13388,7 @@ module.exports = function(edgeVersion) { var localCapabilities = transceiver.localCapabilities; var remoteCapabilities = transceiver.remoteCapabilities; - var rejected = mediaSection.split('\n', 1)[0] - .split(' ', 2)[1] === '0'; + var rejected = SDPUtils.isRejected(mediaSection); if (!rejected && !transceiver.isDatachannel) { var remoteIceParameters = SDPUtils.getIceParameters( @@ -13181,7 +13406,7 @@ module.exports = function(edgeVersion) { } // Calculate intersection of capabilities. - var params = self._getCommonCapabilities(localCapabilities, + var params = getCommonCapabilities(localCapabilities, remoteCapabilities); // Start the RTCRtpSender. The RTCRtpReceiver for this @@ -13241,6 +13466,18 @@ module.exports = function(edgeVersion) { RTCPeerConnection.prototype.setRemoteDescription = function(description) { var self = this; + + if (!isActionAllowedInSignalingState('setRemoteDescription', + description.type, this.signalingState)) { + var e = new Error('Can not set remote ' + description.type + + ' in state ' + this.signalingState); + e.name = 'InvalidStateError'; + if (arguments.length > 2 && typeof arguments[2] === 'function') { + window.setTimeout(arguments[2], 0, e); + } + return Promise.reject(e); + } + var streams = {}; var receiverList = []; var sections = SDPUtils.splitSections(description.sdp); @@ -13249,6 +13486,7 @@ module.exports = function(edgeVersion) { 'a=ice-lite').length > 0; var usingBundle = SDPUtils.matchPrefix(sessionpart, 'a=group:BUNDLE ').length > 0; + this.usingBundle = usingBundle; var iceOptions = SDPUtils.matchPrefix(sessionpart, 'a=ice-options:')[0]; if (iceOptions) { @@ -13260,21 +13498,17 @@ module.exports = function(edgeVersion) { 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 kind = SDPUtils.getKind(mediaSection); + var rejected = SDPUtils.isRejected(mediaSection); + var protocol = lines[0].substr(2).split(' ')[2]; + var direction = SDPUtils.getDirection(mediaSection, sessionpart); var remoteMsid = SDPUtils.parseMsid(mediaSection); - var mid = SDPUtils.matchPrefix(mediaSection, 'a=mid:'); - if (mid.length) { - mid = mid[0].substr(6); - } else { - mid = SDPUtils.generateIdentifier(); - } + var mid = SDPUtils.getMid(mediaSection) || SDPUtils.generateIdentifier(); // Reject datachannels which are not implemented yet. - if (kind === 'application' && mline[2] === 'DTLS/SCTP') { + if (kind === 'application' && protocol === 'DTLS/SCTP') { self.transceivers[sdpMLineIndex] = { mid: mid, isDatachannel: true @@ -13286,7 +13520,6 @@ module.exports = function(edgeVersion) { var iceGatherer; var iceTransport; var dtlsTransport; - var rtpSender; var rtpReceiver; var sendEncodingParameters; var recvEncodingParameters; @@ -13316,20 +13549,45 @@ module.exports = function(edgeVersion) { return SDPUtils.parseCandidate(cand); }) .filter(function(cand) { - return cand.component === '1'; + return cand.component === '1' || cand.component === 1; }); - if (description.type === 'offer' && !rejected) { - var transports = usingBundle && sdpMLineIndex > 0 ? { - iceGatherer: self.transceivers[0].iceGatherer, - iceTransport: self.transceivers[0].iceTransport, - dtlsTransport: self.transceivers[0].dtlsTransport - } : self._createIceAndDtlsTransports(mid, sdpMLineIndex); - if (isComplete && (!usingBundle || sdpMLineIndex === 0)) { - transports.iceTransport.setRemoteCandidates(cands); + // Check if we can use BUNDLE and dispose transports. + if ((description.type === 'offer' || description.type === 'answer') && + !rejected && usingBundle && sdpMLineIndex > 0 && + self.transceivers[sdpMLineIndex]) { + self._disposeIceAndDtlsTransports(sdpMLineIndex); + self.transceivers[sdpMLineIndex].iceGatherer = + self.transceivers[0].iceGatherer; + self.transceivers[sdpMLineIndex].iceTransport = + self.transceivers[0].iceTransport; + self.transceivers[sdpMLineIndex].dtlsTransport = + self.transceivers[0].dtlsTransport; + if (self.transceivers[sdpMLineIndex].rtpSender) { + self.transceivers[sdpMLineIndex].rtpSender.setTransport( + self.transceivers[0].dtlsTransport); + } + if (self.transceivers[sdpMLineIndex].rtpReceiver) { + self.transceivers[sdpMLineIndex].rtpReceiver.setTransport( + self.transceivers[0].dtlsTransport); + } + } + if (description.type === 'offer' && !rejected) { + transceiver = self.transceivers[sdpMLineIndex] || + self._createTransceiver(kind); + transceiver.mid = mid; + + if (!transceiver.iceGatherer) { + transceiver.iceGatherer = usingBundle && sdpMLineIndex > 0 ? + self.transceivers[0].iceGatherer : + self._createIceGatherer(mid, sdpMLineIndex); } - localCapabilities = RTCRtpReceiver.getCapabilities(kind); + if (isComplete && (!usingBundle || sdpMLineIndex === 0)) { + transceiver.iceTransport.setRemoteCandidates(cands); + } + + localCapabilities = window.RTCRtpReceiver.getCapabilities(kind); // filter RTX until additional stuff needed for RTX is implemented // in adapter.js @@ -13345,14 +13603,14 @@ module.exports = function(edgeVersion) { }]; if (direction === 'sendrecv' || direction === 'sendonly') { - rtpReceiver = new RTCRtpReceiver(transports.dtlsTransport, + rtpReceiver = new window.RTCRtpReceiver(transceiver.dtlsTransport, kind); track = rtpReceiver.track; // FIXME: does not work with Plan B. if (remoteMsid) { if (!streams[remoteMsid.stream]) { - streams[remoteMsid.stream] = new MediaStream(); + streams[remoteMsid.stream] = new window.MediaStream(); Object.defineProperty(streams[remoteMsid.stream], 'id', { get: function() { return remoteMsid.stream; @@ -13366,79 +13624,33 @@ module.exports = function(edgeVersion) { }); streams[remoteMsid.stream].addTrack(track); receiverList.push([track, rtpReceiver, - streams[remoteMsid.stream]]); + streams[remoteMsid.stream]]); } else { if (!streams.default) { - streams.default = new MediaStream(); + streams.default = new window.MediaStream(); } streams.default.addTrack(track); receiverList.push([track, rtpReceiver, streams.default]); } } - // FIXME: look at direction. - if (self.localStreams.length > 0 && - self.localStreams[0].getTracks().length >= sdpMLineIndex) { - var localTrack; - if (kind === 'audio') { - localTrack = self.localStreams[0].getAudioTracks()[0]; - } else if (kind === 'video') { - localTrack = self.localStreams[0].getVideoTracks()[0]; - } - if (localTrack) { - // add RTX - if (edgeVersion >= 15019 && kind === 'video') { - sendEncodingParameters[0].rtx = { - ssrc: (2 * sdpMLineIndex + 2) * 1001 + 1 - }; - } - rtpSender = new RTCRtpSender(localTrack, - transports.dtlsTransport); - } - } + transceiver.localCapabilities = localCapabilities; + transceiver.remoteCapabilities = remoteCapabilities; + transceiver.rtpReceiver = rtpReceiver; + transceiver.rtcpParameters = rtcpParameters; + transceiver.sendEncodingParameters = sendEncodingParameters; + transceiver.recvEncodingParameters = recvEncodingParameters; - self.transceivers[sdpMLineIndex] = { - iceGatherer: transports.iceGatherer, - iceTransport: transports.iceTransport, - dtlsTransport: transports.dtlsTransport, - localCapabilities: localCapabilities, - remoteCapabilities: remoteCapabilities, - rtpSender: rtpSender, - rtpReceiver: rtpReceiver, - kind: kind, - mid: mid, - rtcpParameters: rtcpParameters, - 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) { - if (usingBundle && sdpMLineIndex > 0) { - self._disposeIceAndDtlsTransports(sdpMLineIndex); - self.transceivers[sdpMLineIndex].iceGatherer = - self.transceivers[0].iceGatherer; - self.transceivers[sdpMLineIndex].iceTransport = - self.transceivers[0].iceTransport; - self.transceivers[sdpMLineIndex].dtlsTransport = - self.transceivers[0].dtlsTransport; - if (self.transceivers[sdpMLineIndex].rtpSender) { - self.transceivers[sdpMLineIndex].rtpSender.setTransport( - self.transceivers[0].dtlsTransport); - } - if (self.transceivers[sdpMLineIndex].rtpReceiver) { - self.transceivers[sdpMLineIndex].rtpReceiver.setTransport( - self.transceivers[0].dtlsTransport); - } - } 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; @@ -13465,17 +13677,18 @@ module.exports = function(edgeVersion) { if (rtpReceiver && (direction === 'sendrecv' || direction === 'sendonly')) { track = rtpReceiver.track; - receiverList.push([track, rtpReceiver]); if (remoteMsid) { if (!streams[remoteMsid.stream]) { - streams[remoteMsid.stream] = new MediaStream(); + streams[remoteMsid.stream] = new window.MediaStream(); } streams[remoteMsid.stream].addTrack(track); + receiverList.push([track, rtpReceiver, streams[remoteMsid.stream]]); } else { if (!streams.default) { - streams.default = new MediaStream(); + streams.default = new window.MediaStream(); } streams.default.addTrack(track); + receiverList.push([track, rtpReceiver, streams.default]); } } else { // FIXME: actually the receiver should be created later. @@ -13483,7 +13696,6 @@ module.exports = function(edgeVersion) { } } }); - this.usingBundle = usingBundle; this.remoteDescription = { type: description.type, @@ -13532,6 +13744,24 @@ module.exports = function(edgeVersion) { }); } }); + + // check whether addIceCandidate({}) was called within four seconds after + // setRemoteDescription. + window.setTimeout(function() { + if (!(self && self.transceivers)) { + return; + } + self.transceivers.forEach(function(transceiver) { + if (transceiver.iceTransport && + transceiver.iceTransport.state === 'new' && + transceiver.iceTransport.getRemoteCandidates().length > 0) { + console.warn('Timeout for addRemoteCandidate. Consider sending ' + + 'an end-of-candidates notification'); + transceiver.iceTransport.addRemoteCandidate({}); + } + }); + }, 4000); + if (arguments.length > 1 && typeof arguments[1] === 'function') { window.setTimeout(arguments[1], 0); } @@ -13574,12 +13804,22 @@ module.exports = function(edgeVersion) { // Determine whether to fire the negotiationneeded event. RTCPeerConnection.prototype._maybeFireNegotiationNeeded = function() { - // Fire away (for now). - var event = new Event('negotiationneeded'); - this.dispatchEvent(event); - if (this.onnegotiationneeded !== null) { - this.onnegotiationneeded(event); + var self = this; + if (this.signalingState !== 'stable' || this.needNegotiation === true) { + return; } + this.needNegotiation = true; + window.setTimeout(function() { + if (self.needNegotiation === false) { + return; + } + self.needNegotiation = false; + var event = new Event('negotiationneeded'); + self.dispatchEvent(event); + if (self.onnegotiationneeded !== null) { + self.onnegotiationneeded(event); + } + }, 0); }; // Update the connection state. @@ -13593,6 +13833,7 @@ module.exports = function(edgeVersion) { checking: 0, connected: 0, completed: 0, + disconnected: 0, failed: 0 }; this.transceivers.forEach(function(transceiver) { @@ -13637,18 +13878,13 @@ module.exports = function(edgeVersion) { offerOptions = arguments[2]; } - var tracks = []; - var numAudioTracks = 0; - var numVideoTracks = 0; - // Default to sendrecv. - if (this.localStreams.length) { - numAudioTracks = this.localStreams.reduce(function(numTracks, stream) { - return numTracks + stream.getAudioTracks().length; - }, 0); - numVideoTracks = this.localStreams.reduce(function(numTracks, stream) { - return numTracks + stream.getVideoTracks().length; - }, 0); - } + var numAudioTracks = this.transceivers.filter(function(t) { + return t.kind === 'audio'; + }).length; + var numVideoTracks = this.transceivers.filter(function(t) { + return t.kind === 'video'; + }).length; + // Determine number of audio and video tracks we need to send/recv. if (offerOptions) { // Reject Chrome legacy constraints. @@ -13657,67 +13893,69 @@ module.exports = function(edgeVersion) { 'Legacy mandatory/optional constraints not supported.'); } if (offerOptions.offerToReceiveAudio !== undefined) { - numAudioTracks = offerOptions.offerToReceiveAudio; + if (offerOptions.offerToReceiveAudio === true) { + numAudioTracks = 1; + } else if (offerOptions.offerToReceiveAudio === false) { + numAudioTracks = 0; + } else { + numAudioTracks = offerOptions.offerToReceiveAudio; + } } if (offerOptions.offerToReceiveVideo !== undefined) { - numVideoTracks = offerOptions.offerToReceiveVideo; + if (offerOptions.offerToReceiveVideo === true) { + numVideoTracks = 1; + } else if (offerOptions.offerToReceiveVideo === false) { + numVideoTracks = 0; + } else { + numVideoTracks = offerOptions.offerToReceiveVideo; + } } } - // Push local streams. - this.localStreams.forEach(function(localStream) { - localStream.getTracks().forEach(function(track) { - tracks.push({ - kind: track.kind, - track: track, - stream: localStream, - wantReceive: track.kind === 'audio' ? - numAudioTracks > 0 : numVideoTracks > 0 - }); - if (track.kind === 'audio') { - numAudioTracks--; - } else if (track.kind === 'video') { - numVideoTracks--; + this.transceivers.forEach(function(transceiver) { + if (transceiver.kind === 'audio') { + numAudioTracks--; + if (numAudioTracks < 0) { + transceiver.wantReceive = false; } - }); + } else if (transceiver.kind === 'video') { + numVideoTracks--; + if (numVideoTracks < 0) { + transceiver.wantReceive = false; + } + } }); // Create M-lines for recvonly streams. while (numAudioTracks > 0 || numVideoTracks > 0) { if (numAudioTracks > 0) { - tracks.push({ - kind: 'audio', - wantReceive: true - }); + this._createTransceiver('audio'); numAudioTracks--; } if (numVideoTracks > 0) { - tracks.push({ - kind: 'video', - wantReceive: true - }); + this._createTransceiver('video'); numVideoTracks--; } } // reorder tracks - tracks = sortTracks(tracks); + var transceivers = sortTracks(this.transceivers); var sdp = SDPUtils.writeSessionBoilerplate(); - var transceivers = []; - tracks.forEach(function(mline, sdpMLineIndex) { + transceivers.forEach(function(transceiver, sdpMLineIndex) { // For each track, create an ice gatherer, ice transport, // dtls transport, potentially rtpsender and rtpreceiver. - var track = mline.track; - var kind = mline.kind; + var track = transceiver.track; + var kind = transceiver.kind; var mid = SDPUtils.generateIdentifier(); + transceiver.mid = mid; - var transports = self.usingBundle && sdpMLineIndex > 0 ? { - iceGatherer: transceivers[0].iceGatherer, - iceTransport: transceivers[0].iceTransport, - dtlsTransport: transceivers[0].dtlsTransport - } : self._createIceAndDtlsTransports(mid, sdpMLineIndex); + if (!transceiver.iceGatherer) { + transceiver.iceGatherer = self.usingBundle && sdpMLineIndex > 0 ? + transceivers[0].iceGatherer : + self._createIceGatherer(mid, sdpMLineIndex); + } - var localCapabilities = RTCRtpSender.getCapabilities(kind); + var localCapabilities = window.RTCRtpSender.getCapabilities(kind); // filter RTX until additional stuff needed for RTX is implemented // in adapter.js if (edgeVersion < 15019) { @@ -13735,9 +13973,6 @@ module.exports = function(edgeVersion) { } }); - var rtpSender; - var rtpReceiver; - // generate an ssrc now, to be used later in rtpSender.send var sendEncodingParameters = [{ ssrc: (2 * sdpMLineIndex + 1) * 1001 @@ -13749,26 +13984,17 @@ module.exports = function(edgeVersion) { ssrc: (2 * sdpMLineIndex + 1) * 1001 + 1 }; } - rtpSender = new RTCRtpSender(track, transports.dtlsTransport); } - if (mline.wantReceive) { - rtpReceiver = new RTCRtpReceiver(transports.dtlsTransport, kind); + if (transceiver.wantReceive) { + transceiver.rtpReceiver = new window.RTCRtpReceiver( + transceiver.dtlsTransport, + kind + ); } - transceivers[sdpMLineIndex] = { - iceGatherer: transports.iceGatherer, - iceTransport: transports.iceTransport, - dtlsTransport: transports.dtlsTransport, - localCapabilities: localCapabilities, - remoteCapabilities: null, - rtpSender: rtpSender, - rtpReceiver: rtpReceiver, - kind: kind, - mid: mid, - sendEncodingParameters: sendEncodingParameters, - recvEncodingParameters: null - }; + transceiver.localCapabilities = localCapabilities; + transceiver.sendEncodingParameters = sendEncodingParameters; }); // always offer BUNDLE and dispose on return if not supported. @@ -13779,15 +14005,14 @@ module.exports = function(edgeVersion) { } sdp += 'a=ice-options:trickle\r\n'; - tracks.forEach(function(mline, sdpMLineIndex) { - var transceiver = transceivers[sdpMLineIndex]; + transceivers.forEach(function(transceiver, sdpMLineIndex) { sdp += SDPUtils.writeMediaSection(transceiver, - transceiver.localCapabilities, 'offer', mline.stream); + transceiver.localCapabilities, 'offer', transceiver.stream); sdp += 'a=rtcp-rsize\r\n'; }); this._pendingOffer = transceivers; - var desc = new RTCSessionDescription({ + var desc = new window.RTCSessionDescription({ type: 'offer', sdp: sdp }); @@ -13798,23 +14023,40 @@ module.exports = function(edgeVersion) { }; RTCPeerConnection.prototype.createAnswer = function() { - var self = this; - var sdp = SDPUtils.writeSessionBoilerplate(); if (this.usingBundle) { sdp += 'a=group:BUNDLE ' + this.transceivers.map(function(t) { return t.mid; }).join(' ') + '\r\n'; } - this.transceivers.forEach(function(transceiver) { + this.transceivers.forEach(function(transceiver, sdpMLineIndex) { if (transceiver.isDatachannel) { sdp += 'm=application 0 DTLS/SCTP 5000\r\n' + 'c=IN IP4 0.0.0.0\r\n' + 'a=mid:' + transceiver.mid + '\r\n'; return; } + + // FIXME: look at direction. + if (transceiver.stream) { + var localTrack; + if (transceiver.kind === 'audio') { + localTrack = transceiver.stream.getAudioTracks()[0]; + } else if (transceiver.kind === 'video') { + localTrack = transceiver.stream.getVideoTracks()[0]; + } + if (localTrack) { + // add RTX + if (edgeVersion >= 15019 && transceiver.kind === 'video') { + transceiver.sendEncodingParameters[0].rtx = { + ssrc: (2 * sdpMLineIndex + 2) * 1001 + 1 + }; + } + } + } + // Calculate intersection of capabilities. - var commonCapabilities = self._getCommonCapabilities( + var commonCapabilities = getCommonCapabilities( transceiver.localCapabilities, transceiver.remoteCapabilities); @@ -13826,14 +14068,14 @@ module.exports = function(edgeVersion) { } sdp += SDPUtils.writeMediaSection(transceiver, commonCapabilities, - 'answer', self.localStreams[0]); + 'answer', transceiver.stream); if (transceiver.rtcpParameters && transceiver.rtcpParameters.reducedSize) { sdp += 'a=rtcp-rsize\r\n'; } }); - var desc = new RTCSessionDescription({ + var desc = new window.RTCSessionDescription({ type: 'answer', sdp: sdp }); @@ -13870,7 +14112,8 @@ module.exports = function(edgeVersion) { return Promise.resolve(); } // Ignore RTCP candidates, we assume RTCP-MUX. - if (cand.component !== '1') { + if (cand.component && + !(cand.component === '1' || cand.component === 1)) { return Promise.resolve(); } transceiver.iceTransport.addRemoteCandidate(cand); @@ -13892,11 +14135,11 @@ module.exports = function(edgeVersion) { var promises = []; this.transceivers.forEach(function(transceiver) { ['rtpSender', 'rtpReceiver', 'iceGatherer', 'iceTransport', - 'dtlsTransport'].forEach(function(method) { - if (transceiver[method]) { - promises.push(transceiver[method].getStats()); - } - }); + 'dtlsTransport'].forEach(function(method) { + if (transceiver[method]) { + promises.push(transceiver[method].getStats()); + } + }); }); var cb = arguments.length > 1 && typeof arguments[1] === 'function' && arguments[1]; @@ -13929,7 +14172,7 @@ module.exports = function(edgeVersion) { return RTCPeerConnection; }; -},{"sdp":32}],97:[function(require,module,exports){ +},{"sdp":32}],98:[function(require,module,exports){ /* * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. * @@ -13940,10 +14183,10 @@ module.exports = function(edgeVersion) { /* eslint-env node */ 'use strict'; -var browserDetails = require('../utils').browserDetails; +var utils = require('../utils'); var firefoxShim = { - shimOnTrack: function() { + shimOnTrack: function(window) { if (typeof window === 'object' && window.RTCPeerConnection && !('ontrack' in window.RTCPeerConnection.prototype)) { Object.defineProperty(window.RTCPeerConnection.prototype, 'ontrack', { @@ -13970,7 +14213,7 @@ var firefoxShim = { } }, - shimSourceObject: function() { + shimSourceObject: function(window) { // Firefox has supported mozSrcObject since FF22, unprefixed in 42. if (typeof window === 'object') { if (window.HTMLMediaElement && @@ -13988,7 +14231,9 @@ var firefoxShim = { } }, - shimPeerConnection: function() { + shimPeerConnection: function(window) { + var browserDetails = utils.detectBrowser(window); + if (typeof window !== 'object' || !(window.RTCPeerConnection || window.mozRTCPeerConnection)) { return; // probably media.peerconnection.enabled=false in about:config @@ -14021,38 +14266,40 @@ var firefoxShim = { pcConfig.iceServers = newIceServers; } } - return new mozRTCPeerConnection(pcConfig, pcConstraints); + return new window.mozRTCPeerConnection(pcConfig, pcConstraints); }; - window.RTCPeerConnection.prototype = mozRTCPeerConnection.prototype; + window.RTCPeerConnection.prototype = + window.mozRTCPeerConnection.prototype; // wrap static methods. Currently just generateCertificate. - if (mozRTCPeerConnection.generateCertificate) { + if (window.mozRTCPeerConnection.generateCertificate) { Object.defineProperty(window.RTCPeerConnection, 'generateCertificate', { get: function() { - return mozRTCPeerConnection.generateCertificate; + return window.mozRTCPeerConnection.generateCertificate; } }); } - window.RTCSessionDescription = mozRTCSessionDescription; - window.RTCIceCandidate = mozRTCIceCandidate; + window.RTCSessionDescription = window.mozRTCSessionDescription; + window.RTCIceCandidate = window.mozRTCIceCandidate; } // shim away need for obsolete RTCIceCandidate/RTCSessionDescription. ['setLocalDescription', 'setRemoteDescription', 'addIceCandidate'] .forEach(function(method) { - var nativeMethod = RTCPeerConnection.prototype[method]; - RTCPeerConnection.prototype[method] = function() { + var nativeMethod = window.RTCPeerConnection.prototype[method]; + window.RTCPeerConnection.prototype[method] = function() { arguments[0] = new ((method === 'addIceCandidate') ? - RTCIceCandidate : RTCSessionDescription)(arguments[0]); + window.RTCIceCandidate : + window.RTCSessionDescription)(arguments[0]); return nativeMethod.apply(this, arguments); }; }); // support for addIceCandidate(null or undefined) var nativeAddIceCandidate = - RTCPeerConnection.prototype.addIceCandidate; - RTCPeerConnection.prototype.addIceCandidate = function() { + window.RTCPeerConnection.prototype.addIceCandidate; + window.RTCPeerConnection.prototype.addIceCandidate = function() { if (!arguments[0]) { if (arguments[1]) { arguments[1].apply(null); @@ -14080,8 +14327,12 @@ var firefoxShim = { remotecandidate: 'remote-candidate' }; - var nativeGetStats = RTCPeerConnection.prototype.getStats; - RTCPeerConnection.prototype.getStats = function(selector, onSucc, onErr) { + var nativeGetStats = window.RTCPeerConnection.prototype.getStats; + window.RTCPeerConnection.prototype.getStats = function( + selector, + onSucc, + onErr + ) { return nativeGetStats.apply(this, [selector || null]) .then(function(stats) { if (browserDetails.version < 48) { @@ -14121,7 +14372,7 @@ module.exports = { shimGetUserMedia: require('./getusermedia') }; -},{"../utils":100,"./getusermedia":98}],98:[function(require,module,exports){ +},{"../utils":101,"./getusermedia":99}],99:[function(require,module,exports){ /* * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. * @@ -14132,17 +14383,22 @@ module.exports = { /* eslint-env node */ 'use strict'; -var logging = require('../utils').log; -var browserDetails = require('../utils').browserDetails; +var utils = require('../utils'); +var logging = utils.log; // Expose public methods. -module.exports = function() { +module.exports = function(window) { + var browserDetails = utils.detectBrowser(window); + var navigator = window && window.navigator; + var MediaStreamTrack = window && window.MediaStreamTrack; + var shimError_ = function(e) { return { name: { + InternalError: 'NotReadableError', NotSupportedError: 'TypeError', - SecurityError: 'NotAllowedError', - PermissionDeniedError: 'NotAllowedError' + PermissionDeniedError: 'NotAllowedError', + SecurityError: 'NotAllowedError' }[e.name] || e.name, message: { 'The operation is insecure.': 'The request is not allowed by the ' + @@ -14274,6 +14530,48 @@ module.exports = function() { }); }; } + if (!(browserDetails.version > 55 && + 'autoGainControl' in navigator.mediaDevices.getSupportedConstraints())) { + var remap = function(obj, a, b) { + if (a in obj && !(b in obj)) { + obj[b] = obj[a]; + delete obj[a]; + } + }; + + var nativeGetUserMedia = navigator.mediaDevices.getUserMedia. + bind(navigator.mediaDevices); + navigator.mediaDevices.getUserMedia = function(c) { + if (typeof c === 'object' && typeof c.audio === 'object') { + c = JSON.parse(JSON.stringify(c)); + remap(c.audio, 'autoGainControl', 'mozAutoGainControl'); + remap(c.audio, 'noiseSuppression', 'mozNoiseSuppression'); + } + return nativeGetUserMedia(c); + }; + + if (MediaStreamTrack && MediaStreamTrack.prototype.getSettings) { + var nativeGetSettings = MediaStreamTrack.prototype.getSettings; + MediaStreamTrack.prototype.getSettings = function() { + var obj = nativeGetSettings.apply(this, arguments); + remap(obj, 'mozAutoGainControl', 'autoGainControl'); + remap(obj, 'mozNoiseSuppression', 'noiseSuppression'); + return obj; + }; + } + + if (MediaStreamTrack && MediaStreamTrack.prototype.applyConstraints) { + var nativeApplyConstraints = MediaStreamTrack.prototype.applyConstraints; + MediaStreamTrack.prototype.applyConstraints = function(c) { + if (this.kind === 'audio' && typeof c === 'object') { + c = JSON.parse(JSON.stringify(c)); + remap(c, 'autoGainControl', 'mozAutoGainControl'); + remap(c, 'noiseSuppression', 'mozNoiseSuppression'); + } + return nativeApplyConstraints.apply(this, [c]); + }; + } + } navigator.getUserMedia = function(constraints, onSuccess, onError) { if (browserDetails.version < 44) { return getUserMedia_(constraints, onSuccess, onError); @@ -14285,7 +14583,7 @@ module.exports = function() { }; }; -},{"../utils":100}],99:[function(require,module,exports){ +},{"../utils":101}],100:[function(require,module,exports){ /* * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. * @@ -14301,7 +14599,18 @@ var safariShim = { // TODO: check for webkitGTK+ // shimPeerConnection: function() { }, - shimOnAddStream: function() { + shimAddStream: function(window) { + if (typeof window === 'object' && window.RTCPeerConnection && + !('addStream' in window.RTCPeerConnection.prototype)) { + window.RTCPeerConnection.prototype.addStream = function(stream) { + var self = this; + stream.getTracks().forEach(function(track) { + self.addTrack(track, stream); + }); + }; + } + }, + shimOnAddStream: function(window) { if (typeof window === 'object' && window.RTCPeerConnection && !('onaddstream' in window.RTCPeerConnection.prototype)) { Object.defineProperty(window.RTCPeerConnection.prototype, 'onaddstream', { @@ -14331,8 +14640,70 @@ var safariShim = { }); } }, + shimCallbacksAPI: function(window) { + if (typeof window !== 'object' || !window.RTCPeerConnection) { + return; + } + var prototype = window.RTCPeerConnection.prototype; + var createOffer = prototype.createOffer; + var createAnswer = prototype.createAnswer; + var setLocalDescription = prototype.setLocalDescription; + var setRemoteDescription = prototype.setRemoteDescription; + var addIceCandidate = prototype.addIceCandidate; + + prototype.createOffer = function(successCallback, failureCallback) { + var options = (arguments.length >= 2) ? arguments[2] : arguments[0]; + var promise = createOffer.apply(this, [options]); + if (!failureCallback) { + return promise; + } + promise.then(successCallback, failureCallback); + return Promise.resolve(); + }; + + prototype.createAnswer = function(successCallback, failureCallback) { + var options = (arguments.length >= 2) ? arguments[2] : arguments[0]; + var promise = createAnswer.apply(this, [options]); + if (!failureCallback) { + return promise; + } + promise.then(successCallback, failureCallback); + return Promise.resolve(); + }; + + var withCallback = function(description, successCallback, failureCallback) { + var promise = setLocalDescription.apply(this, [description]); + if (!failureCallback) { + return promise; + } + promise.then(successCallback, failureCallback); + return Promise.resolve(); + }; + prototype.setLocalDescription = withCallback; + + withCallback = function(description, successCallback, failureCallback) { + var promise = setRemoteDescription.apply(this, [description]); + if (!failureCallback) { + return promise; + } + promise.then(successCallback, failureCallback); + return Promise.resolve(); + }; + prototype.setRemoteDescription = withCallback; + + withCallback = function(candidate, successCallback, failureCallback) { + var promise = addIceCandidate.apply(this, [candidate]); + if (!failureCallback) { + return promise; + } + promise.then(successCallback, failureCallback); + return Promise.resolve(); + }; + prototype.addIceCandidate = withCallback; + }, + shimGetUserMedia: function(window) { + var navigator = window && window.navigator; - shimGetUserMedia: function() { if (!navigator.getUserMedia) { if (navigator.webkitGetUserMedia) { navigator.getUserMedia = navigator.webkitGetUserMedia.bind(navigator); @@ -14349,13 +14720,15 @@ var safariShim = { // Expose public methods. module.exports = { + shimCallbacksAPI: safariShim.shimCallbacksAPI, + shimAddStream: safariShim.shimAddStream, shimOnAddStream: safariShim.shimOnAddStream, shimGetUserMedia: safariShim.shimGetUserMedia // TODO // shimPeerConnection: safariShim.shimPeerConnection }; -},{}],100:[function(require,module,exports){ +},{}],101:[function(require,module,exports){ /* * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. * @@ -14410,7 +14783,9 @@ var utils = { * @return {object} result containing browser and version * properties. */ - detectBrowser: function() { + detectBrowser: function(window) { + var navigator = window && window.navigator; + // Returned result object. var result = {}; result.browser = null; @@ -14465,7 +14840,9 @@ var utils = { // shimCreateObjectURL must be called before shimSourceObject to avoid loop. - shimCreateObjectURL: function() { + shimCreateObjectURL: function(window) { + var URL = window && window.URL; + if (!(typeof window === 'object' && window.HTMLMediaElement && 'srcObject' in window.HTMLMediaElement.prototype)) { // Only shim CreateObjectURL using srcObject if srcObject exists. @@ -14503,8 +14880,8 @@ var utils = { } }); - var nativeSetAttribute = HTMLMediaElement.prototype.setAttribute; - HTMLMediaElement.prototype.setAttribute = function() { + var nativeSetAttribute = window.HTMLMediaElement.prototype.setAttribute; + window.HTMLMediaElement.prototype.setAttribute = function() { if (arguments.length === 2 && ('' + arguments[0]).toLowerCase() === 'src') { this.srcObject = streams.get(arguments[1]) || null; @@ -14518,13 +14895,12 @@ var utils = { module.exports = { log: utils.log, disableLog: utils.disableLog, - browserDetails: utils.detectBrowser(), extractVersion: utils.extractVersion, shimCreateObjectURL: utils.shimCreateObjectURL, detectBrowser: utils.detectBrowser.bind(utils) }; -},{}],101:[function(require,module,exports){ +},{}],102:[function(require,module,exports){ /* WildEmitter.js is a slim little event emitter by @henrikjoreteg largely based on @visionmedia's Emitter from UI Kit. @@ -14679,7 +15055,7 @@ WildEmitter.mixin = function (constructor) { WildEmitter.mixin(WildEmitter); -},{}],102:[function(require,module,exports){ +},{}],103:[function(require,module,exports){ /*! * EventEmitter v4.2.9 - git.io/ee * Oliver Caldwell @@ -15153,7 +15529,7 @@ WildEmitter.mixin(WildEmitter); } }.call(this)); -},{}],103:[function(require,module,exports){ +},{}],104:[function(require,module,exports){ /** * Module dependencies. @@ -15198,7 +15574,7 @@ function ws(uri, protocols, opts) { if (WebSocket) ws.prototype = WebSocket.prototype; -},{}],104:[function(require,module,exports){ +},{}],105:[function(require,module,exports){ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var OpenVidu_1 = require("./OpenVidu"); @@ -15211,7 +15587,7 @@ if (window) { //Command to generate bundle.js without namespace //watchify Main.ts -p [ tsify ] --exclude kurento-browser-extensions --debug -o ../static/js/OpenVidu.js -v -},{"./OpenVidu":105}],105:[function(require,module,exports){ +},{"./OpenVidu":106}],106:[function(require,module,exports){ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); /* @@ -15309,7 +15685,7 @@ var OpenVidu = (function () { }()); exports.OpenVidu = OpenVidu; -},{"../OpenViduInternal/OpenViduInternal":110,"./Publisher":106,"./Session":107,"webrtc-adapter":91}],106:[function(require,module,exports){ +},{"../OpenViduInternal/OpenViduInternal":111,"./Publisher":107,"./Session":108,"webrtc-adapter":91}],107:[function(require,module,exports){ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var EventEmitter = require("wolfy87-eventemitter"); @@ -15401,7 +15777,7 @@ var Publisher = (function () { }()); exports.Publisher = Publisher; -},{"wolfy87-eventemitter":102}],107:[function(require,module,exports){ +},{"wolfy87-eventemitter":103}],108:[function(require,module,exports){ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var Subscriber_1 = require("./Subscriber"); @@ -15424,8 +15800,10 @@ var Session = (function () { s = _a[_i]; s.removeVideo(); } - for (var streamId in _this.connection.getStreams()) { - _this.connection.getStreams()[streamId].removeVideo(); + if (_this.connection) { + for (var streamId in _this.connection.getStreams()) { + _this.connection.getStreams()[streamId].removeVideo(); + } } }); // Sets or updates the value of 'connection' property. Triggered by SessionInternal when succesful connection @@ -15589,7 +15967,7 @@ var Session = (function () { }()); exports.Session = Session; -},{"./Subscriber":108,"wolfy87-eventemitter":102}],108:[function(require,module,exports){ +},{"./Subscriber":109,"wolfy87-eventemitter":103}],109:[function(require,module,exports){ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var EventEmitter = require("wolfy87-eventemitter"); @@ -15627,7 +16005,7 @@ var Subscriber = (function () { }()); exports.Subscriber = Subscriber; -},{"wolfy87-eventemitter":102}],109:[function(require,module,exports){ +},{"wolfy87-eventemitter":103}],110:[function(require,module,exports){ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var Stream_1 = require("./Stream"); @@ -15694,7 +16072,7 @@ var Connection = (function () { }()); exports.Connection = Connection; -},{"./Stream":112}],110:[function(require,module,exports){ +},{"./Stream":113}],111:[function(require,module,exports){ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); /* @@ -15728,7 +16106,8 @@ var OpenViduInternal = (function () { } OpenViduInternal.prototype.checkNgrokUri = function () { if (this.wsUri.indexOf(".ngrok.io") !== -1) { - // OpenVidu server URL referes to a ngrok IP: delete port of URL + // OpenVidu server URL referes to a ngrok IP: secure wss protocol and delete port of URL + this.wsUri = this.wsUri.replace("ws://", "wss://"); var regex = /\.ngrok\.io:\d+/; this.wsUri = this.wsUri.replace(regex, ".ngrok.io"); } @@ -16038,7 +16417,7 @@ var OpenViduInternal = (function () { }()); exports.OpenViduInternal = OpenViduInternal; -},{"./SessionInternal":111,"./Stream":112,"kurento-jsonrpc":14}],111:[function(require,module,exports){ +},{"./SessionInternal":112,"./Stream":113,"kurento-jsonrpc":14}],112:[function(require,module,exports){ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var Connection_1 = require("./Connection"); @@ -16453,7 +16832,7 @@ var SessionInternal = (function () { }()); exports.SessionInternal = SessionInternal; -},{"./Connection":109,"wolfy87-eventemitter":102}],112:[function(require,module,exports){ +},{"./Connection":110,"wolfy87-eventemitter":103}],113:[function(require,module,exports){ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var EventEmitter = require("wolfy87-eventemitter"); @@ -16616,6 +16995,7 @@ var Stream = (function () { hide('progress-' + spinnerId); }; Stream.prototype.playOnlyVideo = function (parentElement, thumbnailId) { + // TO-DO: check somehow if the stream is audio only, so the element created is