2019-01-26 02:03:01 +08:00
( 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 ( ) { function r ( e , n , t ) { function o ( i , f ) { if ( ! n [ i ] ) { if ( ! e [ i ] ) { var c = "function" == typeof require && require ; if ( ! f && c ) return c ( i , ! 0 ) ; if ( u ) return u ( i , ! 0 ) ; var a = new Error ( "Cannot find module '" + i + "'" ) ; throw a . code = "MODULE_NOT_FOUND" , a } var p = n [ i ] = { exports : { } } ; e [ i ] [ 0 ] . call ( p . exports , function ( r ) { var n = e [ i ] [ 1 ] [ r ] ; return o ( n || r ) } , p , p . exports , r , e , n , t ) } return n [ i ] . exports } for ( var u = "function" == typeof require && require , i = 0 ; i < t . length ; i ++ ) o ( t [ i ] ) ; return o } return r } ) ( ) ( { 1 : [ 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 _adapter _factory = require ( './adapter_factory.js' ) ;
var adapter = ( 0 , _adapter _factory . adapterFactory ) ( { window : window } ) ;
module . exports = adapter ; // this is the difference from adapter_core.
} , { "./adapter_factory.js" : 2 } ] , 2 : [ function ( require , module , exports ) {
'use strict' ;
Object . defineProperty ( exports , "__esModule" , {
value : true
2017-11-28 00:22:48 +08:00
} ) ;
2019-01-26 02:03:01 +08:00
exports . adapterFactory = adapterFactory ;
var _utils = require ( './utils' ) ;
var utils = _interopRequireWildcard ( _utils ) ;
var _chrome _shim = require ( './chrome/chrome_shim' ) ;
var chromeShim = _interopRequireWildcard ( _chrome _shim ) ;
var _edge _shim = require ( './edge/edge_shim' ) ;
var edgeShim = _interopRequireWildcard ( _edge _shim ) ;
var _firefox _shim = require ( './firefox/firefox_shim' ) ;
var firefoxShim = _interopRequireWildcard ( _firefox _shim ) ;
var _safari _shim = require ( './safari/safari_shim' ) ;
var safariShim = _interopRequireWildcard ( _safari _shim ) ;
var _common _shim = require ( './common_shim' ) ;
var commonShim = _interopRequireWildcard ( _common _shim ) ;
function _interopRequireWildcard ( obj ) { if ( obj && obj . _ _esModule ) { return obj ; } else { var newObj = { } ; if ( obj != null ) { for ( var key in obj ) { if ( Object . prototype . hasOwnProperty . call ( obj , key ) ) newObj [ key ] = obj [ key ] ; } } newObj . default = obj ; return newObj ; } }
// Shimming starts here.
/ *
* 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 .
* /
function adapterFactory ( ) {
var _ref = arguments . length > 0 && arguments [ 0 ] !== undefined ? arguments [ 0 ] : { } ,
window = _ref . window ;
var options = arguments . length > 1 && arguments [ 1 ] !== undefined ? arguments [ 1 ] : {
shimChrome : true ,
shimFirefox : true ,
shimEdge : true ,
shimSafari : true
} ;
// Utils.
var logging = utils . log ;
var browserDetails = utils . detectBrowser ( window ) ;
var adapter = {
browserDetails : browserDetails ,
commonShim : commonShim ,
extractVersion : utils . extractVersion ,
disableLog : utils . disableLog ,
disableWarnings : utils . disableWarnings
} ;
// Shim browser if found.
switch ( browserDetails . browser ) {
case 'chrome' :
if ( ! chromeShim || ! chromeShim . shimPeerConnection || ! options . shimChrome ) {
logging ( 'Chrome shim is not included in this adapter release.' ) ;
return adapter ;
2017-11-28 00:22:48 +08:00
}
2019-01-26 02:03:01 +08:00
logging ( 'adapter.js shimming chrome.' ) ;
// Export to the adapter global object visible in the browser.
adapter . browserShim = chromeShim ;
chromeShim . shimGetUserMedia ( window ) ;
chromeShim . shimMediaStream ( window ) ;
chromeShim . shimPeerConnection ( window ) ;
chromeShim . shimOnTrack ( window ) ;
chromeShim . shimAddTrackRemoveTrack ( window ) ;
chromeShim . shimGetSendersWithDtmf ( window ) ;
chromeShim . shimSenderReceiverGetStats ( window ) ;
chromeShim . fixNegotiationNeeded ( window ) ;
commonShim . shimRTCIceCandidate ( window ) ;
commonShim . shimConnectionState ( window ) ;
commonShim . shimMaxMessageSize ( window ) ;
commonShim . shimSendThrowTypeError ( window ) ;
2017-11-28 00:22:48 +08:00
break ;
2019-01-26 02:03:01 +08:00
case 'firefox' :
if ( ! firefoxShim || ! firefoxShim . shimPeerConnection || ! options . shimFirefox ) {
logging ( 'Firefox shim is not included in this adapter release.' ) ;
return adapter ;
}
logging ( 'adapter.js shimming firefox.' ) ;
// Export to the adapter global object visible in the browser.
adapter . browserShim = firefoxShim ;
firefoxShim . shimGetUserMedia ( window ) ;
firefoxShim . shimPeerConnection ( window ) ;
firefoxShim . shimOnTrack ( window ) ;
firefoxShim . shimRemoveStream ( window ) ;
firefoxShim . shimSenderGetStats ( window ) ;
firefoxShim . shimReceiverGetStats ( window ) ;
firefoxShim . shimRTCDataChannel ( window ) ;
commonShim . shimRTCIceCandidate ( window ) ;
commonShim . shimConnectionState ( window ) ;
commonShim . shimMaxMessageSize ( window ) ;
commonShim . shimSendThrowTypeError ( window ) ;
2017-11-28 00:22:48 +08:00
break ;
2019-01-26 02:03:01 +08:00
case 'edge' :
if ( ! edgeShim || ! edgeShim . shimPeerConnection || ! options . shimEdge ) {
logging ( 'MS edge shim is not included in this adapter release.' ) ;
return adapter ;
}
logging ( 'adapter.js shimming edge.' ) ;
// Export to the adapter global object visible in the browser.
adapter . browserShim = edgeShim ;
edgeShim . shimGetUserMedia ( window ) ;
edgeShim . shimGetDisplayMedia ( window ) ;
edgeShim . shimPeerConnection ( window ) ;
edgeShim . shimReplaceTrack ( window ) ;
// the edge shim implements the full RTCIceCandidate object.
commonShim . shimMaxMessageSize ( window ) ;
commonShim . shimSendThrowTypeError ( window ) ;
2017-11-28 00:22:48 +08:00
break ;
2019-01-26 02:03:01 +08:00
case 'safari' :
if ( ! safariShim || ! options . shimSafari ) {
logging ( 'Safari shim is not included in this adapter release.' ) ;
return adapter ;
}
logging ( 'adapter.js shimming safari.' ) ;
// Export to the adapter global object visible in the browser.
adapter . browserShim = safariShim ;
safariShim . shimRTCIceServerUrls ( window ) ;
safariShim . shimCreateOfferLegacy ( window ) ;
safariShim . shimCallbacksAPI ( window ) ;
safariShim . shimLocalStreamsAPI ( window ) ;
safariShim . shimRemoteStreamsAPI ( window ) ;
safariShim . shimTrackEventTransceiver ( window ) ;
safariShim . shimGetUserMedia ( window ) ;
commonShim . shimRTCIceCandidate ( window ) ;
commonShim . shimMaxMessageSize ( window ) ;
commonShim . shimSendThrowTypeError ( window ) ;
2017-11-28 00:22:48 +08:00
break ;
default :
2019-01-26 02:03:01 +08:00
logging ( 'Unsupported browser!' ) ;
2017-11-28 00:22:48 +08:00
break ;
}
2019-01-26 02:03:01 +08:00
return adapter ;
}
// Browser shims.
} , { "./chrome/chrome_shim" : 3 , "./common_shim" : 6 , "./edge/edge_shim" : 7 , "./firefox/firefox_shim" : 11 , "./safari/safari_shim" : 14 , "./utils" : 15 } ] , 3 : [ 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' ;
Object . defineProperty ( exports , "__esModule" , {
value : true
} ) ;
exports . shimGetDisplayMedia = exports . shimGetUserMedia = undefined ;
var _typeof = typeof Symbol === "function" && typeof Symbol . iterator === "symbol" ? function ( obj ) { return typeof obj ; } : function ( obj ) { return obj && typeof Symbol === "function" && obj . constructor === Symbol && obj !== Symbol . prototype ? "symbol" : typeof obj ; } ;
var _getusermedia = require ( './getusermedia' ) ;
Object . defineProperty ( exports , 'shimGetUserMedia' , {
enumerable : true ,
get : function get ( ) {
return _getusermedia . shimGetUserMedia ;
2017-11-28 00:22:48 +08:00
}
2019-01-26 02:03:01 +08:00
} ) ;
var _getdisplaymedia = require ( './getdisplaymedia' ) ;
Object . defineProperty ( exports , 'shimGetDisplayMedia' , {
enumerable : true ,
get : function get ( ) {
return _getdisplaymedia . shimGetDisplayMedia ;
2017-11-28 00:22:48 +08:00
}
2019-01-26 02:03:01 +08:00
} ) ;
exports . shimMediaStream = shimMediaStream ;
exports . shimOnTrack = shimOnTrack ;
exports . shimGetSendersWithDtmf = shimGetSendersWithDtmf ;
exports . shimSenderReceiverGetStats = shimSenderReceiverGetStats ;
exports . shimAddTrackRemoveTrackWithNative = shimAddTrackRemoveTrackWithNative ;
exports . shimAddTrackRemoveTrack = shimAddTrackRemoveTrack ;
exports . shimPeerConnection = shimPeerConnection ;
exports . fixNegotiationNeeded = fixNegotiationNeeded ;
var _utils = require ( '../utils.js' ) ;
var utils = _interopRequireWildcard ( _utils ) ;
function _interopRequireWildcard ( obj ) { if ( obj && obj . _ _esModule ) { return obj ; } else { var newObj = { } ; if ( obj != null ) { for ( var key in obj ) { if ( Object . prototype . hasOwnProperty . call ( obj , key ) ) newObj [ key ] = obj [ key ] ; } } newObj . default = obj ; return newObj ; } }
/* iterates the stats graph recursively. */
function walkStats ( stats , base , resultSet ) {
if ( ! base || resultSet . has ( base . id ) ) {
return ;
2017-11-28 00:22:48 +08:00
}
2019-01-26 02:03:01 +08:00
resultSet . set ( base . id , base ) ;
Object . keys ( base ) . forEach ( function ( name ) {
if ( name . endsWith ( 'Id' ) ) {
walkStats ( stats , stats . get ( base [ name ] ) , resultSet ) ;
} else if ( name . endsWith ( 'Ids' ) ) {
base [ name ] . forEach ( function ( id ) {
walkStats ( stats , stats . get ( id ) , resultSet ) ;
2017-11-28 00:22:48 +08:00
} ) ;
}
2019-01-26 02:03:01 +08:00
} ) ;
}
/* filter getStats for a sender/receiver track. */
function filterStats ( result , track , outbound ) {
var streamStatsType = outbound ? 'outbound-rtp' : 'inbound-rtp' ;
var filteredResult = new Map ( ) ;
if ( track === null ) {
return filteredResult ;
2017-11-28 00:22:48 +08:00
}
2019-01-26 02:03:01 +08:00
var trackStats = [ ] ;
result . forEach ( function ( value ) {
if ( value . type === 'track' && value . trackIdentifier === track . id ) {
trackStats . push ( value ) ;
2017-11-28 00:22:48 +08:00
}
2019-01-26 02:03:01 +08:00
} ) ;
trackStats . forEach ( function ( trackStat ) {
result . forEach ( function ( stats ) {
if ( stats . type === streamStatsType && stats . trackId === trackStat . id ) {
walkStats ( result , stats , filteredResult ) ;
2017-11-28 00:22:48 +08:00
}
} ) ;
2019-01-26 02:03:01 +08:00
} ) ;
return filteredResult ;
}
function shimMediaStream ( window ) {
window . MediaStream = window . MediaStream || window . webkitMediaStream ;
}
function shimOnTrack ( window ) {
if ( ( typeof window === 'undefined' ? 'undefined' : _typeof ( window ) ) === 'object' && window . RTCPeerConnection && ! ( 'ontrack' in window . RTCPeerConnection . prototype ) ) {
Object . defineProperty ( window . RTCPeerConnection . prototype , 'ontrack' , {
get : function get ( ) {
return this . _ontrack ;
} ,
set : function set ( f ) {
if ( this . _ontrack ) {
this . removeEventListener ( 'track' , this . _ontrack ) ;
}
this . addEventListener ( 'track' , this . _ontrack = f ) ;
} ,
enumerable : true ,
configurable : true
} ) ;
var origSetRemoteDescription = window . RTCPeerConnection . prototype . setRemoteDescription ;
window . RTCPeerConnection . prototype . setRemoteDescription = function ( ) {
var _this = this ;
if ( ! this . _ontrackpoly ) {
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.
e . stream . addEventListener ( 'addtrack' , function ( te ) {
var receiver = void 0 ;
if ( window . RTCPeerConnection . prototype . getReceivers ) {
receiver = _this . getReceivers ( ) . find ( function ( r ) {
return r . track && r . track . id === te . track . id ;
} ) ;
} else {
receiver = { track : te . track } ;
}
var event = new Event ( 'track' ) ;
event . track = te . track ;
event . receiver = receiver ;
event . transceiver = { receiver : receiver } ;
event . streams = [ e . stream ] ;
_this . dispatchEvent ( event ) ;
} ) ;
e . stream . getTracks ( ) . forEach ( function ( track ) {
var receiver = void 0 ;
if ( window . RTCPeerConnection . prototype . getReceivers ) {
receiver = _this . getReceivers ( ) . find ( function ( r ) {
return r . track && r . track . id === track . id ;
} ) ;
} else {
receiver = { track : track } ;
}
var event = new Event ( 'track' ) ;
event . track = track ;
event . receiver = receiver ;
event . transceiver = { receiver : receiver } ;
event . streams = [ e . stream ] ;
_this . dispatchEvent ( event ) ;
} ) ;
} ;
this . addEventListener ( 'addstream' , this . _ontrackpoly ) ;
2017-11-28 00:22:48 +08:00
}
2019-01-26 02:03:01 +08:00
return origSetRemoteDescription . apply ( this , arguments ) ;
} ;
} else {
// even if RTCRtpTransceiver is in window, it is only used and
// emitted in unified-plan. Unfortunately this means we need
// to unconditionally wrap the event.
utils . wrapPeerConnectionEvent ( window , 'track' , function ( e ) {
if ( ! e . transceiver ) {
Object . defineProperty ( e , 'transceiver' , { value : { receiver : e . receiver } } ) ;
2017-11-28 00:22:48 +08:00
}
2019-01-26 02:03:01 +08:00
return e ;
2017-11-28 00:22:48 +08:00
} ) ;
}
2019-01-26 02:03:01 +08:00
}
function shimGetSendersWithDtmf ( window ) {
// Overrides addTrack/removeTrack, depends on shimAddTrackRemoveTrack.
if ( ( typeof window === 'undefined' ? 'undefined' : _typeof ( window ) ) === 'object' && window . RTCPeerConnection && ! ( 'getSenders' in window . RTCPeerConnection . prototype ) && 'createDTMFSender' in window . RTCPeerConnection . prototype ) {
var shimSenderWithDtmf = function shimSenderWithDtmf ( pc , track ) {
return {
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 : pc
} ;
2017-11-28 00:22:48 +08:00
} ;
2019-01-26 02:03:01 +08:00
// augment addTrack when getSenders is not available.
if ( ! window . RTCPeerConnection . prototype . getSenders ) {
window . RTCPeerConnection . prototype . getSenders = function ( ) {
this . _senders = this . _senders || [ ] ;
return this . _senders . slice ( ) ; // return a copy of the internal state.
} ;
var origAddTrack = window . RTCPeerConnection . prototype . addTrack ;
window . RTCPeerConnection . prototype . addTrack = function ( track , stream ) {
var sender = origAddTrack . apply ( this , arguments ) ;
if ( ! sender ) {
sender = shimSenderWithDtmf ( this , track ) ;
this . _senders . push ( sender ) ;
}
return sender ;
} ;
var origRemoveTrack = window . RTCPeerConnection . prototype . removeTrack ;
window . RTCPeerConnection . prototype . removeTrack = function ( sender ) {
origRemoveTrack . apply ( this , arguments ) ;
var idx = this . _senders . indexOf ( sender ) ;
if ( idx !== - 1 ) {
this . _senders . splice ( idx , 1 ) ;
}
} ;
2017-11-28 00:22:48 +08:00
}
2019-01-26 02:03:01 +08:00
var origAddStream = window . RTCPeerConnection . prototype . addStream ;
window . RTCPeerConnection . prototype . addStream = function ( stream ) {
var _this2 = this ;
this . _senders = this . _senders || [ ] ;
origAddStream . apply ( this , [ stream ] ) ;
stream . getTracks ( ) . forEach ( function ( track ) {
_this2 . _senders . push ( shimSenderWithDtmf ( _this2 , track ) ) ;
2017-11-28 00:22:48 +08:00
} ) ;
} ;
2019-01-26 02:03:01 +08:00
var origRemoveStream = window . RTCPeerConnection . prototype . removeStream ;
window . RTCPeerConnection . prototype . removeStream = function ( stream ) {
var _this3 = this ;
this . _senders = this . _senders || [ ] ;
origRemoveStream . apply ( this , [ stream ] ) ;
stream . getTracks ( ) . forEach ( function ( track ) {
var sender = _this3 . _senders . find ( function ( s ) {
return s . track === track ;
} ) ;
if ( sender ) {
// remove sender
_this3 . _senders . splice ( _this3 . _senders . indexOf ( sender ) , 1 ) ;
2017-11-28 00:22:48 +08:00
}
2019-01-26 02:03:01 +08:00
} ) ;
} ;
} else if ( ( typeof window === 'undefined' ? 'undefined' : _typeof ( window ) ) === 'object' && window . RTCPeerConnection && 'getSenders' in window . RTCPeerConnection . prototype && 'createDTMFSender' in window . RTCPeerConnection . prototype && window . RTCRtpSender && ! ( 'dtmf' in window . RTCRtpSender . prototype ) ) {
var origGetSenders = window . RTCPeerConnection . prototype . getSenders ;
window . RTCPeerConnection . prototype . getSenders = function ( ) {
var _this4 = this ;
var senders = origGetSenders . apply ( this , [ ] ) ;
senders . forEach ( function ( sender ) {
return sender . _pc = _this4 ;
} ) ;
return senders ;
} ;
Object . defineProperty ( window . RTCRtpSender . prototype , 'dtmf' , {
get : function get ( ) {
if ( this . _dtmf === undefined ) {
if ( this . track . kind === 'audio' ) {
this . _dtmf = this . _pc . createDTMFSender ( this . track ) ;
} else {
this . _dtmf = null ;
2017-11-28 00:22:48 +08:00
}
}
2019-01-26 02:03:01 +08:00
return this . _dtmf ;
2017-11-28 00:22:48 +08:00
}
} ) ;
}
2019-01-26 02:03:01 +08:00
}
function shimSenderReceiverGetStats ( window ) {
if ( ! ( ( typeof window === 'undefined' ? 'undefined' : _typeof ( window ) ) === 'object' && window . RTCPeerConnection && window . RTCRtpSender && window . RTCRtpReceiver ) ) {
return ;
2017-11-28 00:22:48 +08:00
}
2019-01-26 02:03:01 +08:00
// shim sender stats.
if ( ! ( 'getStats' in window . RTCRtpSender . prototype ) ) {
var origGetSenders = window . RTCPeerConnection . prototype . getSenders ;
if ( origGetSenders ) {
window . RTCPeerConnection . prototype . getSenders = function ( ) {
var _this5 = this ;
var senders = origGetSenders . apply ( this , [ ] ) ;
senders . forEach ( function ( sender ) {
return sender . _pc = _this5 ;
} ) ;
return senders ;
2017-11-28 00:22:48 +08:00
} ;
}
2019-01-26 02:03:01 +08:00
var origAddTrack = window . RTCPeerConnection . prototype . addTrack ;
if ( origAddTrack ) {
window . RTCPeerConnection . prototype . addTrack = function ( ) {
var sender = origAddTrack . apply ( this , arguments ) ;
sender . _pc = this ;
return sender ;
} ;
2017-11-28 00:22:48 +08:00
}
2019-01-26 02:03:01 +08:00
window . RTCRtpSender . prototype . getStats = function ( ) {
var sender = this ;
return this . _pc . getStats ( ) . then ( function ( result ) {
return (
/ * N o t e : t h i s w i l l i n c l u d e s t a t s o f a l l s e n d e r s t h a t
* send a track with the same id as sender . track as
* it is not possible to identify the RTCRtpSender .
* /
filterStats ( result , sender . track , true )
) ;
} ) ;
} ;
}
// shim receiver stats.
if ( ! ( 'getStats' in window . RTCRtpReceiver . prototype ) ) {
var origGetReceivers = window . RTCPeerConnection . prototype . getReceivers ;
if ( origGetReceivers ) {
window . RTCPeerConnection . prototype . getReceivers = function ( ) {
var _this6 = this ;
var receivers = origGetReceivers . apply ( this , [ ] ) ;
receivers . forEach ( function ( receiver ) {
return receiver . _pc = _this6 ;
2017-11-28 00:22:48 +08:00
} ) ;
2019-01-26 02:03:01 +08:00
return receivers ;
} ;
2017-11-28 00:22:48 +08:00
}
2019-01-26 02:03:01 +08:00
utils . wrapPeerConnectionEvent ( window , 'track' , function ( e ) {
e . receiver . _pc = e . srcElement ;
return e ;
} ) ;
window . RTCRtpReceiver . prototype . getStats = function ( ) {
var receiver = this ;
return this . _pc . getStats ( ) . then ( function ( result ) {
return filterStats ( result , receiver . track , false ) ;
} ) ;
} ;
}
if ( ! ( 'getStats' in window . RTCRtpSender . prototype && 'getStats' in window . RTCRtpReceiver . prototype ) ) {
return ;
}
// shim RTCPeerConnection.getStats(track).
var origGetStats = window . RTCPeerConnection . prototype . getStats ;
window . RTCPeerConnection . prototype . getStats = function ( ) {
if ( arguments . length > 0 && arguments [ 0 ] instanceof window . MediaStreamTrack ) {
var track = arguments [ 0 ] ;
var sender = void 0 ;
var receiver = void 0 ;
var err = void 0 ;
this . getSenders ( ) . forEach ( function ( s ) {
if ( s . track === track ) {
if ( sender ) {
err = true ;
2017-11-28 00:22:48 +08:00
} else {
2019-01-26 02:03:01 +08:00
sender = s ;
2017-11-28 00:22:48 +08:00
}
}
2019-01-26 02:03:01 +08:00
} ) ;
this . getReceivers ( ) . forEach ( function ( r ) {
if ( r . track === track ) {
if ( receiver ) {
err = true ;
} else {
receiver = r ;
2017-11-28 00:22:48 +08:00
}
}
2019-01-26 02:03:01 +08:00
return r . track === track ;
} ) ;
if ( err || sender && receiver ) {
return Promise . reject ( new DOMException ( 'There are more than one sender or receiver for the track.' , 'InvalidAccessError' ) ) ;
} else if ( sender ) {
return sender . getStats ( ) ;
} else if ( receiver ) {
return receiver . getStats ( ) ;
2017-11-28 00:22:48 +08:00
}
2019-01-26 02:03:01 +08:00
return Promise . reject ( new DOMException ( 'There is no sender or receiver for the track.' , 'InvalidAccessError' ) ) ;
2017-11-28 00:22:48 +08:00
}
2019-01-26 02:03:01 +08:00
return origGetStats . apply ( this , arguments ) ;
2017-11-28 00:22:48 +08:00
} ;
2019-01-26 02:03:01 +08:00
}
function shimAddTrackRemoveTrackWithNative ( window ) {
// shim addTrack/removeTrack with native variants in order to make
// the interactions with legacy getLocalStreams behave as in other browsers.
// Keeps a mapping stream.id => [stream, rtpsenders...]
window . RTCPeerConnection . prototype . getLocalStreams = function ( ) {
var _this7 = this ;
this . _shimmedLocalStreams = this . _shimmedLocalStreams || { } ;
return Object . keys ( this . _shimmedLocalStreams ) . map ( function ( streamId ) {
return _this7 . _shimmedLocalStreams [ streamId ] [ 0 ] ;
} ) ;
} ;
var origAddTrack = window . RTCPeerConnection . prototype . addTrack ;
window . RTCPeerConnection . prototype . addTrack = function ( track , stream ) {
if ( ! stream ) {
return origAddTrack . apply ( this , arguments ) ;
}
this . _shimmedLocalStreams = this . _shimmedLocalStreams || { } ;
var sender = origAddTrack . apply ( this , arguments ) ;
if ( ! this . _shimmedLocalStreams [ stream . id ] ) {
this . _shimmedLocalStreams [ stream . id ] = [ stream , sender ] ;
} else if ( this . _shimmedLocalStreams [ stream . id ] . indexOf ( sender ) === - 1 ) {
this . _shimmedLocalStreams [ stream . id ] . push ( sender ) ;
}
return sender ;
} ;
var origAddStream = window . RTCPeerConnection . prototype . addStream ;
window . RTCPeerConnection . prototype . addStream = function ( stream ) {
var _this8 = this ;
this . _shimmedLocalStreams = this . _shimmedLocalStreams || { } ;
stream . getTracks ( ) . forEach ( function ( track ) {
var alreadyExists = _this8 . getSenders ( ) . find ( function ( s ) {
return s . track === track ;
} ) ;
if ( alreadyExists ) {
throw new DOMException ( 'Track already exists.' , 'InvalidAccessError' ) ;
2017-11-28 00:22:48 +08:00
}
2019-01-26 02:03:01 +08:00
} ) ;
var existingSenders = this . getSenders ( ) ;
origAddStream . apply ( this , arguments ) ;
var newSenders = this . getSenders ( ) . filter ( function ( newSender ) {
return existingSenders . indexOf ( newSender ) === - 1 ;
} ) ;
this . _shimmedLocalStreams [ stream . id ] = [ stream ] . concat ( newSenders ) ;
} ;
var origRemoveStream = window . RTCPeerConnection . prototype . removeStream ;
window . RTCPeerConnection . prototype . removeStream = function ( stream ) {
this . _shimmedLocalStreams = this . _shimmedLocalStreams || { } ;
delete this . _shimmedLocalStreams [ stream . id ] ;
return origRemoveStream . apply ( this , arguments ) ;
} ;
var origRemoveTrack = window . RTCPeerConnection . prototype . removeTrack ;
window . RTCPeerConnection . prototype . removeTrack = function ( sender ) {
var _this9 = this ;
this . _shimmedLocalStreams = this . _shimmedLocalStreams || { } ;
if ( sender ) {
Object . keys ( this . _shimmedLocalStreams ) . forEach ( function ( streamId ) {
var idx = _this9 . _shimmedLocalStreams [ streamId ] . indexOf ( sender ) ;
if ( idx !== - 1 ) {
_this9 . _shimmedLocalStreams [ streamId ] . splice ( idx , 1 ) ;
}
if ( _this9 . _shimmedLocalStreams [ streamId ] . length === 1 ) {
delete _this9 . _shimmedLocalStreams [ streamId ] ;
2017-11-28 00:22:48 +08:00
}
} ) ;
}
2019-01-26 02:03:01 +08:00
return origRemoveTrack . apply ( this , arguments ) ;
} ;
}
function shimAddTrackRemoveTrack ( window ) {
if ( ! window . RTCPeerConnection ) {
return ;
}
var browserDetails = utils . detectBrowser ( window ) ;
// shim addTrack and removeTrack.
if ( window . RTCPeerConnection . prototype . addTrack && browserDetails . version >= 65 ) {
return shimAddTrackRemoveTrackWithNative ( window ) ;
}
// also shim pc.getLocalStreams when addTrack is shimmed
// to return the original streams.
var origGetLocalStreams = window . RTCPeerConnection . prototype . getLocalStreams ;
window . RTCPeerConnection . prototype . getLocalStreams = function ( ) {
var _this10 = this ;
var nativeStreams = origGetLocalStreams . apply ( this ) ;
this . _reverseStreams = this . _reverseStreams || { } ;
return nativeStreams . map ( function ( stream ) {
return _this10 . _reverseStreams [ stream . id ] ;
} ) ;
} ;
var origAddStream = window . RTCPeerConnection . prototype . addStream ;
window . RTCPeerConnection . prototype . addStream = function ( stream ) {
var _this11 = this ;
this . _streams = this . _streams || { } ;
this . _reverseStreams = this . _reverseStreams || { } ;
stream . getTracks ( ) . forEach ( function ( track ) {
var alreadyExists = _this11 . getSenders ( ) . find ( function ( s ) {
return s . track === track ;
} ) ;
if ( alreadyExists ) {
throw new DOMException ( 'Track already exists.' , 'InvalidAccessError' ) ;
2017-11-28 00:22:48 +08:00
}
} ) ;
2019-01-26 02:03:01 +08:00
// Add identity mapping for consistency with addTrack.
// Unless this is being used with a stream from addTrack.
if ( ! this . _reverseStreams [ stream . id ] ) {
var newStream = new window . MediaStream ( stream . getTracks ( ) ) ;
this . _streams [ stream . id ] = newStream ;
this . _reverseStreams [ newStream . id ] = stream ;
stream = newStream ;
2017-11-28 00:22:48 +08:00
}
2019-01-26 02:03:01 +08:00
origAddStream . apply ( this , [ stream ] ) ;
} ;
var origRemoveStream = window . RTCPeerConnection . prototype . removeStream ;
window . RTCPeerConnection . prototype . removeStream = function ( stream ) {
this . _streams = this . _streams || { } ;
this . _reverseStreams = this . _reverseStreams || { } ;
origRemoveStream . apply ( this , [ this . _streams [ stream . id ] || stream ] ) ;
delete this . _reverseStreams [ this . _streams [ stream . id ] ? this . _streams [ stream . id ] . id : stream . id ] ;
delete this . _streams [ stream . id ] ;
} ;
window . RTCPeerConnection . prototype . addTrack = function ( track , stream ) {
var _this12 = this ;
if ( this . signalingState === 'closed' ) {
throw new DOMException ( 'The RTCPeerConnection\'s signalingState is \'closed\'.' , 'InvalidStateError' ) ;
2017-11-28 00:22:48 +08:00
}
2019-01-26 02:03:01 +08:00
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' ) ;
2017-11-28 00:22:48 +08:00
}
2019-01-26 02:03:01 +08:00
var alreadyExists = this . getSenders ( ) . find ( function ( s ) {
return s . track === track ;
} ) ;
if ( alreadyExists ) {
throw new DOMException ( 'Track already exists.' , 'InvalidAccessError' ) ;
2017-11-28 00:22:48 +08:00
}
2019-01-26 02:03:01 +08:00
this . _streams = this . _streams || { } ;
this . _reverseStreams = this . _reverseStreams || { } ;
var oldStream = this . _streams [ stream . id ] ;
if ( oldStream ) {
// this is using odd Chrome behaviour, use with caution:
// https://bugs.chromium.org/p/webrtc/issues/detail?id=7815
// Note: we rely on the high-level addTrack/dtmf shim to
// create the sender with a dtmf sender.
oldStream . addTrack ( track ) ;
// Trigger ONN async.
Promise . resolve ( ) . then ( function ( ) {
_this12 . dispatchEvent ( new Event ( 'negotiationneeded' ) ) ;
} ) ;
} else {
var newStream = new window . MediaStream ( [ track ] ) ;
this . _streams [ stream . id ] = newStream ;
this . _reverseStreams [ newStream . id ] = stream ;
this . addStream ( newStream ) ;
2017-11-28 00:22:48 +08:00
}
2019-01-26 02:03:01 +08:00
return this . getSenders ( ) . find ( function ( s ) {
return s . track === track ;
} ) ;
} ;
// replace the internal stream id with the external one and
// vice versa.
function replaceInternalStreamId ( pc , description ) {
var sdp = description . sdp ;
Object . keys ( pc . _reverseStreams || [ ] ) . forEach ( function ( internalId ) {
var externalStream = pc . _reverseStreams [ internalId ] ;
var internalStream = pc . _streams [ externalStream . id ] ;
sdp = sdp . replace ( new RegExp ( internalStream . id , 'g' ) , externalStream . id ) ;
} ) ;
return new RTCSessionDescription ( {
type : description . type ,
sdp : sdp
} ) ;
2017-11-28 00:22:48 +08:00
}
2019-01-26 02:03:01 +08:00
function replaceExternalStreamId ( pc , description ) {
var sdp = description . sdp ;
Object . keys ( pc . _reverseStreams || [ ] ) . forEach ( function ( internalId ) {
var externalStream = pc . _reverseStreams [ internalId ] ;
var internalStream = pc . _streams [ externalStream . id ] ;
sdp = sdp . replace ( new RegExp ( externalStream . id , 'g' ) , internalStream . id ) ;
} ) ;
return new RTCSessionDescription ( {
type : description . type ,
sdp : sdp
} ) ;
2017-11-28 00:22:48 +08:00
}
2019-01-26 02:03:01 +08:00
[ 'createOffer' , 'createAnswer' ] . forEach ( function ( method ) {
var nativeMethod = window . RTCPeerConnection . prototype [ method ] ;
window . RTCPeerConnection . prototype [ method ] = function ( ) {
var _this13 = this ;
var args = arguments ;
var isLegacyCall = arguments . length && typeof arguments [ 0 ] === 'function' ;
if ( isLegacyCall ) {
return nativeMethod . apply ( this , [ function ( description ) {
var desc = replaceInternalStreamId ( _this13 , description ) ;
args [ 0 ] . apply ( null , [ desc ] ) ;
} , function ( err ) {
if ( args [ 1 ] ) {
args [ 1 ] . apply ( null , err ) ;
}
} , arguments [ 2 ] ] ) ;
}
return nativeMethod . apply ( this , arguments ) . then ( function ( description ) {
return replaceInternalStreamId ( _this13 , description ) ;
} ) ;
} ;
} ) ;
var origSetLocalDescription = window . RTCPeerConnection . prototype . setLocalDescription ;
window . RTCPeerConnection . prototype . setLocalDescription = function ( ) {
if ( ! arguments . length || ! arguments [ 0 ] . type ) {
return origSetLocalDescription . apply ( this , arguments ) ;
2017-11-28 00:22:48 +08:00
}
2019-01-26 02:03:01 +08:00
arguments [ 0 ] = replaceExternalStreamId ( this , arguments [ 0 ] ) ;
return origSetLocalDescription . apply ( this , arguments ) ;
2017-11-28 00:22:48 +08:00
} ;
2019-01-26 02:03:01 +08:00
// TODO: mangle getStats: https://w3c.github.io/webrtc-stats/#dom-rtcmediastreamstats-streamidentifier
var origLocalDescription = Object . getOwnPropertyDescriptor ( window . RTCPeerConnection . prototype , 'localDescription' ) ;
Object . defineProperty ( window . RTCPeerConnection . prototype , 'localDescription' , {
get : function get ( ) {
var description = origLocalDescription . get . apply ( this ) ;
if ( description . type === '' ) {
return description ;
}
return replaceInternalStreamId ( this , description ) ;
}
2017-11-28 00:22:48 +08:00
} ) ;
2019-01-26 02:03:01 +08:00
window . RTCPeerConnection . prototype . removeTrack = function ( sender ) {
var _this14 = this ;
if ( this . signalingState === 'closed' ) {
throw new DOMException ( 'The RTCPeerConnection\'s signalingState is \'closed\'.' , 'InvalidStateError' ) ;
2017-11-28 00:22:48 +08:00
}
2019-01-26 02:03:01 +08:00
// We can not yet check for sender instanceof RTCRtpSender
// since we shim RTPSender. So we check if sender._pc is set.
if ( ! sender . _pc ) {
throw new DOMException ( 'Argument 1 of RTCPeerConnection.removeTrack ' + 'does not implement interface RTCRtpSender.' , 'TypeError' ) ;
2017-11-28 00:22:48 +08:00
}
2019-01-26 02:03:01 +08:00
var isLocal = sender . _pc === this ;
if ( ! isLocal ) {
throw new DOMException ( 'Sender was not created by this connection.' , 'InvalidAccessError' ) ;
2017-11-28 00:22:48 +08:00
}
2019-01-26 02:03:01 +08:00
// Search for the native stream the senders track belongs to.
this . _streams = this . _streams || { } ;
var stream = void 0 ;
Object . keys ( this . _streams ) . forEach ( function ( streamid ) {
var hasTrack = _this14 . _streams [ streamid ] . getTracks ( ) . find ( function ( track ) {
return sender . track === track ;
} ) ;
if ( hasTrack ) {
stream = _this14 . _streams [ streamid ] ;
}
} ) ;
if ( stream ) {
if ( stream . getTracks ( ) . length === 1 ) {
// if this is the last track of the stream, remove the stream. This
// takes care of any shimmed _senders.
this . removeStream ( this . _reverseStreams [ stream . id ] ) ;
2017-11-28 00:22:48 +08:00
} else {
2019-01-26 02:03:01 +08:00
// relying on the same odd chrome behaviour as above.
stream . removeTrack ( sender . track ) ;
2017-11-28 00:22:48 +08:00
}
2019-01-26 02:03:01 +08:00
this . dispatchEvent ( new Event ( 'negotiationneeded' ) ) ;
2017-11-28 00:22:48 +08:00
}
2019-01-26 02:03:01 +08:00
} ;
}
function shimPeerConnection ( window ) {
if ( ! window . RTCPeerConnection && window . webkitRTCPeerConnection ) {
// very basic support for old versions.
window . RTCPeerConnection = window . webkitRTCPeerConnection ;
2017-11-28 00:22:48 +08:00
}
2019-01-26 02:03:01 +08:00
if ( ! window . RTCPeerConnection ) {
return ;
2017-11-28 00:22:48 +08:00
}
2019-01-26 02:03:01 +08:00
var origGetStats = window . RTCPeerConnection . prototype . getStats ;
window . RTCPeerConnection . prototype . getStats = function ( selector , successCallback , errorCallback ) {
var _this15 = this ;
var args = arguments ;
// If selector is a function then we are in the old style stats so just
// pass back the original getStats format to avoid breaking old users.
if ( arguments . length > 0 && typeof selector === 'function' ) {
return origGetStats . apply ( this , arguments ) ;
2017-11-28 00:22:48 +08:00
}
2019-01-26 02:03:01 +08:00
// When spec-style getStats is supported, return those when called with
// either no arguments or the selector argument is null.
if ( origGetStats . length === 0 && ( arguments . length === 0 || typeof arguments [ 0 ] !== 'function' ) ) {
return origGetStats . apply ( this , [ ] ) ;
2017-11-28 00:22:48 +08:00
}
2019-01-26 02:03:01 +08:00
var fixChromeStats _ = function fixChromeStats _ ( response ) {
var standardReport = { } ;
var reports = response . result ( ) ;
reports . forEach ( function ( report ) {
var standardStats = {
id : report . id ,
timestamp : report . timestamp ,
type : {
localcandidate : 'local-candidate' ,
remotecandidate : 'remote-candidate'
} [ report . type ] || report . type
2017-11-28 00:22:48 +08:00
} ;
2019-01-26 02:03:01 +08:00
report . names ( ) . forEach ( function ( name ) {
standardStats [ name ] = report . stat ( name ) ;
} ) ;
standardReport [ standardStats . id ] = standardStats ;
} ) ;
return standardReport ;
} ;
// shim getStats with maplike support
var makeMapStats = function makeMapStats ( stats ) {
return new Map ( Object . keys ( stats ) . map ( function ( key ) {
return [ key , stats [ key ] ] ;
} ) ) ;
} ;
if ( arguments . length >= 2 ) {
var successCallbackWrapper _ = function successCallbackWrapper _ ( response ) {
args [ 1 ] ( makeMapStats ( fixChromeStats _ ( response ) ) ) ;
} ;
return origGetStats . apply ( this , [ successCallbackWrapper _ , arguments [ 0 ] ] ) ;
2017-11-28 00:22:48 +08:00
}
2019-01-26 02:03:01 +08:00
// promise-support
return new Promise ( function ( resolve , reject ) {
origGetStats . apply ( _this15 , [ function ( response ) {
resolve ( makeMapStats ( fixChromeStats _ ( response ) ) ) ;
} , reject ] ) ;
} ) . then ( successCallback , errorCallback ) ;
} ;
// shim implicit creation of RTCSessionDescription/RTCIceCandidate
[ 'setLocalDescription' , 'setRemoteDescription' , 'addIceCandidate' ] . forEach ( function ( method ) {
var nativeMethod = window . RTCPeerConnection . prototype [ method ] ;
window . RTCPeerConnection . prototype [ method ] = function ( ) {
arguments [ 0 ] = new ( method === 'addIceCandidate' ? window . RTCIceCandidate : window . RTCSessionDescription ) ( arguments [ 0 ] ) ;
return nativeMethod . apply ( this , arguments ) ;
} ;
2017-11-28 00:22:48 +08:00
} ) ;
2019-01-26 02:03:01 +08:00
// support for addIceCandidate(null or undefined)
var nativeAddIceCandidate = window . RTCPeerConnection . prototype . addIceCandidate ;
window . RTCPeerConnection . prototype . addIceCandidate = function ( ) {
if ( ! arguments [ 0 ] ) {
if ( arguments [ 1 ] ) {
arguments [ 1 ] . apply ( null ) ;
2017-11-28 00:22:48 +08:00
}
2019-01-26 02:03:01 +08:00
return Promise . resolve ( ) ;
2017-11-28 00:22:48 +08:00
}
2019-01-26 02:03:01 +08:00
return nativeAddIceCandidate . apply ( this , arguments ) ;
} ;
}
function fixNegotiationNeeded ( window ) {
utils . wrapPeerConnectionEvent ( window , 'negotiationneeded' , function ( e ) {
var pc = e . target ;
if ( pc . signalingState !== 'stable' ) {
2017-11-28 00:22:48 +08:00
return ;
}
2019-01-26 02:03:01 +08:00
return e ;
2017-11-28 00:22:48 +08:00
} ) ;
2019-01-26 02:03:01 +08:00
}
} , { "../utils.js" : 15 , "./getdisplaymedia" : 4 , "./getusermedia" : 5 } ] , 4 : [ function ( require , module , exports ) {
/ *
* Copyright ( c ) 2018 The adapter . js 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' ;
Object . defineProperty ( exports , "__esModule" , {
value : true
} ) ;
exports . shimGetDisplayMedia = shimGetDisplayMedia ;
function shimGetDisplayMedia ( window , getSourceId ) {
if ( window . navigator . mediaDevices && 'getDisplayMedia' in window . navigator . mediaDevices ) {
return ;
2017-11-28 00:22:48 +08:00
}
2019-01-26 02:03:01 +08:00
if ( ! window . navigator . mediaDevices ) {
return ;
}
// getSourceId is a function that returns a promise resolving with
// the sourceId of the screen/window/tab to be shared.
if ( typeof getSourceId !== 'function' ) {
console . error ( 'shimGetDisplayMedia: getSourceId argument is not ' + 'a function' ) ;
return ;
}
window . navigator . mediaDevices . getDisplayMedia = function ( constraints ) {
return getSourceId ( constraints ) . then ( function ( sourceId ) {
var widthSpecified = constraints . video && constraints . video . width ;
var heightSpecified = constraints . video && constraints . video . height ;
var frameRateSpecified = constraints . video && constraints . video . frameRate ;
constraints . video = {
mandatory : {
chromeMediaSource : 'desktop' ,
chromeMediaSourceId : sourceId ,
maxFrameRate : frameRateSpecified || 3
2017-11-28 00:22:48 +08:00
}
2019-01-26 02:03:01 +08:00
} ;
if ( widthSpecified ) {
constraints . video . mandatory . maxWidth = widthSpecified ;
2017-11-28 00:22:48 +08:00
}
2019-01-26 02:03:01 +08:00
if ( heightSpecified ) {
constraints . video . mandatory . maxHeight = heightSpecified ;
2017-11-28 00:22:48 +08:00
}
2019-01-26 02:03:01 +08:00
return window . navigator . mediaDevices . getUserMedia ( constraints ) ;
} ) ;
} ;
}
} , { } ] , 5 : [ 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' ;
Object . defineProperty ( exports , "__esModule" , {
value : true
} ) ;
var _typeof = typeof Symbol === "function" && typeof Symbol . iterator === "symbol" ? function ( obj ) { return typeof obj ; } : function ( obj ) { return obj && typeof Symbol === "function" && obj . constructor === Symbol && obj !== Symbol . prototype ? "symbol" : typeof obj ; } ;
exports . shimGetUserMedia = shimGetUserMedia ;
var _utils = require ( '../utils.js' ) ;
var utils = _interopRequireWildcard ( _utils ) ;
function _interopRequireWildcard ( obj ) { if ( obj && obj . _ _esModule ) { return obj ; } else { var newObj = { } ; if ( obj != null ) { for ( var key in obj ) { if ( Object . prototype . hasOwnProperty . call ( obj , key ) ) newObj [ key ] = obj [ key ] ; } } newObj . default = obj ; return newObj ; } }
var logging = utils . log ;
function shimGetUserMedia ( window ) {
var navigator = window && window . navigator ;
if ( ! navigator . mediaDevices ) {
return ;
}
var browserDetails = utils . detectBrowser ( window ) ;
var constraintsToChrome _ = function constraintsToChrome _ ( c ) {
if ( ( typeof c === 'undefined' ? 'undefined' : _typeof ( c ) ) !== 'object' || c . mandatory || c . optional ) {
return c ;
2017-11-28 00:22:48 +08:00
}
2019-01-26 02:03:01 +08:00
var cc = { } ;
Object . keys ( c ) . forEach ( function ( key ) {
if ( key === 'require' || key === 'advanced' || key === 'mediaSource' ) {
return ;
2017-11-28 00:22:48 +08:00
}
2019-01-26 02:03:01 +08:00
var r = _typeof ( c [ key ] ) === 'object' ? c [ key ] : { ideal : c [ key ] } ;
if ( r . exact !== undefined && typeof r . exact === 'number' ) {
r . min = r . max = r . exact ;
2017-11-28 00:22:48 +08:00
}
2019-01-26 02:03:01 +08:00
var oldname _ = function oldname _ ( prefix , name ) {
if ( prefix ) {
return prefix + name . charAt ( 0 ) . toUpperCase ( ) + name . slice ( 1 ) ;
}
return name === 'deviceId' ? 'sourceId' : name ;
} ;
if ( r . ideal !== undefined ) {
cc . optional = cc . optional || [ ] ;
var oc = { } ;
if ( typeof r . ideal === 'number' ) {
oc [ oldname _ ( 'min' , key ) ] = r . ideal ;
cc . optional . push ( oc ) ;
oc = { } ;
oc [ oldname _ ( 'max' , key ) ] = r . ideal ;
cc . optional . push ( oc ) ;
} else {
oc [ oldname _ ( '' , key ) ] = r . ideal ;
cc . optional . push ( oc ) ;
}
2017-11-28 00:22:48 +08:00
}
2019-01-26 02:03:01 +08:00
if ( r . exact !== undefined && typeof r . exact !== 'number' ) {
cc . mandatory = cc . mandatory || { } ;
cc . mandatory [ oldname _ ( '' , key ) ] = r . exact ;
} else {
[ 'min' , 'max' ] . forEach ( function ( mix ) {
if ( r [ mix ] !== undefined ) {
cc . mandatory = cc . mandatory || { } ;
cc . mandatory [ oldname _ ( mix , key ) ] = r [ mix ] ;
}
} ) ;
2017-11-28 00:22:48 +08:00
}
2019-01-26 02:03:01 +08:00
} ) ;
if ( c . advanced ) {
cc . optional = ( cc . optional || [ ] ) . concat ( c . advanced ) ;
}
return cc ;
} ;
var shimConstraints _ = function shimConstraints _ ( constraints , func ) {
if ( browserDetails . version >= 61 ) {
return func ( constraints ) ;
}
constraints = JSON . parse ( JSON . stringify ( constraints ) ) ;
if ( constraints && _typeof ( constraints . audio ) === 'object' ) {
var remap = function remap ( obj , a , b ) {
if ( a in obj && ! ( b in obj ) ) {
obj [ b ] = obj [ a ] ;
delete obj [ a ] ;
2017-11-28 00:22:48 +08:00
}
2019-01-26 02:03:01 +08:00
} ;
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' ) {
// Shim facingMode for mobile & surface pro.
var face = constraints . video . facingMode ;
face = face && ( ( typeof face === 'undefined' ? 'undefined' : _typeof ( face ) ) === 'object' ? face : { ideal : face } ) ;
var getSupportedFacingModeLies = browserDetails . version < 66 ;
if ( face && ( face . exact === 'user' || face . exact === 'environment' || face . ideal === 'user' || face . ideal === 'environment' ) && ! ( navigator . mediaDevices . getSupportedConstraints && navigator . mediaDevices . getSupportedConstraints ( ) . facingMode && ! getSupportedFacingModeLies ) ) {
delete constraints . video . facingMode ;
var matches = void 0 ;
if ( face . exact === 'environment' || face . ideal === 'environment' ) {
matches = [ 'back' , 'rear' ] ;
} else if ( face . exact === 'user' || face . ideal === 'user' ) {
matches = [ 'front' ] ;
}
if ( matches ) {
// Look for matches in label, or use last cam for back (typical).
return navigator . mediaDevices . enumerateDevices ( ) . then ( function ( devices ) {
devices = devices . filter ( function ( d ) {
return d . kind === 'videoinput' ;
} ) ;
var dev = devices . find ( function ( d ) {
return matches . some ( function ( match ) {
return d . label . toLowerCase ( ) . includes ( match ) ;
} ) ;
} ) ;
if ( ! dev && devices . length && matches . includes ( 'back' ) ) {
dev = devices [ devices . length - 1 ] ; // more likely the back cam
}
if ( dev ) {
constraints . video . deviceId = face . exact ? { exact : dev . deviceId } : { ideal : dev . deviceId } ;
}
constraints . video = constraintsToChrome _ ( constraints . video ) ;
logging ( 'chrome: ' + JSON . stringify ( constraints ) ) ;
return func ( constraints ) ;
} ) ;
2017-11-28 00:22:48 +08:00
}
}
2019-01-26 02:03:01 +08:00
constraints . video = constraintsToChrome _ ( constraints . video ) ;
2017-11-28 00:22:48 +08:00
}
2019-01-26 02:03:01 +08:00
logging ( 'chrome: ' + JSON . stringify ( constraints ) ) ;
return func ( constraints ) ;
} ;
var shimError _ = function shimError _ ( e ) {
if ( browserDetails . version >= 64 ) {
return e ;
2017-11-28 00:22:48 +08:00
}
return {
2019-01-26 02:03:01 +08:00
name : {
PermissionDeniedError : 'NotAllowedError' ,
PermissionDismissedError : 'NotAllowedError' ,
InvalidStateError : 'NotAllowedError' ,
DevicesNotFoundError : 'NotFoundError' ,
ConstraintNotSatisfiedError : 'OverconstrainedError' ,
TrackStartError : 'NotReadableError' ,
MediaDeviceFailedDueToShutdown : 'NotAllowedError' ,
MediaDeviceKillSwitchOn : 'NotAllowedError' ,
TabCaptureError : 'AbortError' ,
ScreenCaptureError : 'AbortError' ,
DeviceCaptureError : 'AbortError'
} [ e . name ] || e . name ,
message : e . message ,
constraint : e . constraint || e . constraintName ,
toString : function toString ( ) {
return this . name + ( this . message && ': ' ) + this . message ;
}
} ;
2017-11-28 00:22:48 +08:00
} ;
2019-01-26 02:03:01 +08:00
var getUserMedia _ = function getUserMedia _ ( constraints , onSuccess , onError ) {
shimConstraints _ ( constraints , function ( c ) {
navigator . webkitGetUserMedia ( c , onSuccess , function ( e ) {
if ( onError ) {
onError ( shimError _ ( e ) ) ;
}
2017-11-28 00:22:48 +08:00
} ) ;
} ) ;
2019-01-26 02:03:01 +08:00
} ;
navigator . getUserMedia = getUserMedia _ . bind ( navigator ) ;
// Even though Chrome 45 has navigator.mediaDevices and a getUserMedia
// function which returns a Promise, it does not accept spec-style
// constraints.
var origGetUserMedia = navigator . mediaDevices . getUserMedia . bind ( navigator . mediaDevices ) ;
navigator . mediaDevices . getUserMedia = function ( cs ) {
return shimConstraints _ ( cs , function ( c ) {
return origGetUserMedia ( c ) . then ( function ( stream ) {
if ( c . audio && ! stream . getAudioTracks ( ) . length || c . video && ! stream . getVideoTracks ( ) . length ) {
stream . getTracks ( ) . forEach ( function ( track ) {
track . stop ( ) ;
} ) ;
throw new DOMException ( '' , 'NotFoundError' ) ;
}
return stream ;
} , function ( e ) {
return Promise . reject ( shimError _ ( e ) ) ;
} ) ;
} ) ;
} ;
2017-11-28 00:22:48 +08:00
}
2019-01-26 02:03:01 +08:00
} , { "../utils.js" : 15 } ] , 6 : [ function ( require , module , exports ) {
/ *
* Copyright ( c ) 2017 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' ;
Object . defineProperty ( exports , "__esModule" , {
value : true
2017-11-28 00:22:48 +08:00
} ) ;
2019-01-26 02:03:01 +08:00
var _typeof = typeof Symbol === "function" && typeof Symbol . iterator === "symbol" ? function ( obj ) { return typeof obj ; } : function ( obj ) { return obj && typeof Symbol === "function" && obj . constructor === Symbol && obj !== Symbol . prototype ? "symbol" : typeof obj ; } ;
exports . shimRTCIceCandidate = shimRTCIceCandidate ;
exports . shimMaxMessageSize = shimMaxMessageSize ;
exports . shimSendThrowTypeError = shimSendThrowTypeError ;
exports . shimConnectionState = shimConnectionState ;
var _sdp = require ( 'sdp' ) ;
var _sdp2 = _interopRequireDefault ( _sdp ) ;
var _utils = require ( './utils' ) ;
var utils = _interopRequireWildcard ( _utils ) ;
function _interopRequireWildcard ( obj ) { if ( obj && obj . _ _esModule ) { return obj ; } else { var newObj = { } ; if ( obj != null ) { for ( var key in obj ) { if ( Object . prototype . hasOwnProperty . call ( obj , key ) ) newObj [ key ] = obj [ key ] ; } } newObj . default = obj ; return newObj ; } }
function _interopRequireDefault ( obj ) { return obj && obj . _ _esModule ? obj : { default : obj } ; }
function shimRTCIceCandidate ( window ) {
// foundation is arbitrarily chosen as an indicator for full support for
// https://w3c.github.io/webrtc-pc/#rtcicecandidate-interface
if ( ! window . RTCIceCandidate || window . RTCIceCandidate && 'foundation' in window . RTCIceCandidate . prototype ) {
return ;
2017-11-28 00:22:48 +08:00
}
2019-01-26 02:03:01 +08:00
var NativeRTCIceCandidate = window . RTCIceCandidate ;
window . RTCIceCandidate = function ( args ) {
// Remove the a= which shouldn't be part of the candidate string.
if ( ( typeof args === 'undefined' ? 'undefined' : _typeof ( args ) ) === 'object' && args . candidate && args . candidate . indexOf ( 'a=' ) === 0 ) {
args = JSON . parse ( JSON . stringify ( args ) ) ;
args . candidate = args . candidate . substr ( 2 ) ;
}
if ( args . candidate && args . candidate . length ) {
// Augment the native candidate with the parsed fields.
var nativeCandidate = new NativeRTCIceCandidate ( args ) ;
var parsedCandidate = _sdp2 . default . parseCandidate ( args . candidate ) ;
var augmentedCandidate = Object . assign ( nativeCandidate , parsedCandidate ) ;
// Add a serializer that does not serialize the extra attributes.
augmentedCandidate . toJSON = function ( ) {
return {
candidate : augmentedCandidate . candidate ,
sdpMid : augmentedCandidate . sdpMid ,
sdpMLineIndex : augmentedCandidate . sdpMLineIndex ,
usernameFragment : augmentedCandidate . usernameFragment
} ;
} ;
return augmentedCandidate ;
}
return new NativeRTCIceCandidate ( args ) ;
} ;
window . RTCIceCandidate . prototype = NativeRTCIceCandidate . prototype ;
// Hook up the augmented candidate in onicecandidate and
// addEventListener('icecandidate', ...)
utils . wrapPeerConnectionEvent ( window , 'icecandidate' , function ( e ) {
if ( e . candidate ) {
Object . defineProperty ( e , 'candidate' , {
value : new window . RTCIceCandidate ( e . candidate ) ,
writable : 'false'
} ) ;
}
return e ;
} ) ;
2017-11-28 00:22:48 +08:00
}
2019-01-26 02:03:01 +08:00
function shimMaxMessageSize ( window ) {
if ( window . RTCSctpTransport || ! window . RTCPeerConnection ) {
return ;
2017-11-28 00:22:48 +08:00
}
2019-01-26 02:03:01 +08:00
var browserDetails = utils . detectBrowser ( window ) ;
if ( ! ( 'sctp' in window . RTCPeerConnection . prototype ) ) {
Object . defineProperty ( window . RTCPeerConnection . prototype , 'sctp' , {
get : function get ( ) {
return typeof this . _sctp === 'undefined' ? null : this . _sctp ;
}
} ) ;
2017-11-28 00:22:48 +08:00
}
2019-01-26 02:03:01 +08:00
var sctpInDescription = function sctpInDescription ( description ) {
var sections = _sdp2 . default . splitSections ( description . sdp ) ;
sections . shift ( ) ;
return sections . some ( function ( mediaSection ) {
var mLine = _sdp2 . default . parseMLine ( mediaSection ) ;
return mLine && mLine . kind === 'application' && mLine . protocol . indexOf ( 'SCTP' ) !== - 1 ;
} ) ;
} ;
var getRemoteFirefoxVersion = function getRemoteFirefoxVersion ( description ) {
// TODO: Is there a better solution for detecting Firefox?
var match = description . sdp . match ( /mozilla...THIS_IS_SDPARTA-(\d+)/ ) ;
if ( match === null || match . length < 2 ) {
return - 1 ;
}
var version = parseInt ( match [ 1 ] , 10 ) ;
// Test for NaN (yes, this is ugly)
return version !== version ? - 1 : version ;
} ;
var getCanSendMaxMessageSize = function getCanSendMaxMessageSize ( remoteIsFirefox ) {
// Every implementation we know can send at least 64 KiB.
// Note: Although Chrome is technically able to send up to 256 KiB, the
// data does not reach the other peer reliably.
// See: https://bugs.chromium.org/p/webrtc/issues/detail?id=8419
var canSendMaxMessageSize = 65536 ;
if ( browserDetails . browser === 'firefox' ) {
if ( browserDetails . version < 57 ) {
if ( remoteIsFirefox === - 1 ) {
// FF < 57 will send in 16 KiB chunks using the deprecated PPID
// fragmentation.
canSendMaxMessageSize = 16384 ;
} else {
// However, other FF (and RAWRTC) can reassemble PPID-fragmented
// messages. Thus, supporting ~2 GiB when sending.
canSendMaxMessageSize = 2147483637 ;
}
} else if ( browserDetails . version < 60 ) {
// Currently, all FF >= 57 will reset the remote maximum message size
// to the default value when a data channel is created at a later
// stage. :(
// See: https://bugzilla.mozilla.org/show_bug.cgi?id=1426831
canSendMaxMessageSize = browserDetails . version === 57 ? 65535 : 65536 ;
} else {
// FF >= 60 supports sending ~2 GiB
canSendMaxMessageSize = 2147483637 ;
}
}
return canSendMaxMessageSize ;
} ;
var getMaxMessageSize = function getMaxMessageSize ( description , remoteIsFirefox ) {
// Note: 65536 bytes is the default value from the SDP spec. Also,
// every implementation we know supports receiving 65536 bytes.
var maxMessageSize = 65536 ;
// FF 57 has a slightly incorrect default remote max message size, so
// we need to adjust it here to avoid a failure when sending.
// See: https://bugzilla.mozilla.org/show_bug.cgi?id=1425697
if ( browserDetails . browser === 'firefox' && browserDetails . version === 57 ) {
maxMessageSize = 65535 ;
}
var match = _sdp2 . default . matchPrefix ( description . sdp , 'a=max-message-size:' ) ;
if ( match . length > 0 ) {
maxMessageSize = parseInt ( match [ 0 ] . substr ( 19 ) , 10 ) ;
} else if ( browserDetails . browser === 'firefox' && remoteIsFirefox !== - 1 ) {
// If the maximum message size is not present in the remote SDP and
// both local and remote are Firefox, the remote peer can receive
// ~2 GiB.
maxMessageSize = 2147483637 ;
}
return maxMessageSize ;
} ;
var origSetRemoteDescription = window . RTCPeerConnection . prototype . setRemoteDescription ;
window . RTCPeerConnection . prototype . setRemoteDescription = function ( ) {
this . _sctp = null ;
if ( sctpInDescription ( arguments [ 0 ] ) ) {
// Check if the remote is FF.
var isFirefox = getRemoteFirefoxVersion ( arguments [ 0 ] ) ;
// Get the maximum message size the local peer is capable of sending
var canSendMMS = getCanSendMaxMessageSize ( isFirefox ) ;
// Get the maximum message size of the remote peer.
var remoteMMS = getMaxMessageSize ( arguments [ 0 ] , isFirefox ) ;
// Determine final maximum message size
var maxMessageSize = void 0 ;
if ( canSendMMS === 0 && remoteMMS === 0 ) {
maxMessageSize = Number . POSITIVE _INFINITY ;
} else if ( canSendMMS === 0 || remoteMMS === 0 ) {
maxMessageSize = Math . max ( canSendMMS , remoteMMS ) ;
} else {
maxMessageSize = Math . min ( canSendMMS , remoteMMS ) ;
}
// Create a dummy RTCSctpTransport object and the 'maxMessageSize'
// attribute.
var sctp = { } ;
Object . defineProperty ( sctp , 'maxMessageSize' , {
get : function get ( ) {
return maxMessageSize ;
}
} ) ;
this . _sctp = sctp ;
}
return origSetRemoteDescription . apply ( this , arguments ) ;
} ;
2017-11-28 00:22:48 +08:00
}
2019-01-26 02:03:01 +08:00
function shimSendThrowTypeError ( window ) {
if ( ! ( window . RTCPeerConnection && 'createDataChannel' in window . RTCPeerConnection . prototype ) ) {
return ;
}
// Note: Although Firefox >= 57 has a native implementation, the maximum
// message size can be reset for all data channels at a later stage.
// See: https://bugzilla.mozilla.org/show_bug.cgi?id=1426831
function wrapDcSend ( dc , pc ) {
var origDataChannelSend = dc . send ;
dc . send = function ( ) {
var data = arguments [ 0 ] ;
var length = data . length || data . size || data . byteLength ;
if ( dc . readyState === 'open' && pc . sctp && length > pc . sctp . maxMessageSize ) {
throw new TypeError ( 'Message too large (can send a maximum of ' + pc . sctp . maxMessageSize + ' bytes)' ) ;
2017-11-28 00:22:48 +08:00
}
2019-01-26 02:03:01 +08:00
return origDataChannelSend . apply ( dc , arguments ) ;
2017-11-28 00:22:48 +08:00
} ;
}
2019-01-26 02:03:01 +08:00
var origCreateDataChannel = window . RTCPeerConnection . prototype . createDataChannel ;
window . RTCPeerConnection . prototype . createDataChannel = function ( ) {
var dataChannel = origCreateDataChannel . apply ( this , arguments ) ;
wrapDcSend ( dataChannel , this ) ;
return dataChannel ;
} ;
utils . wrapPeerConnectionEvent ( window , 'datachannel' , function ( e ) {
wrapDcSend ( e . channel , e . target ) ;
return e ;
2017-11-28 00:22:48 +08:00
} ) ;
}
2019-01-26 02:03:01 +08:00
/ * s h i m s R T C C o n n e c t i o n S t a t e b y p r e t e n d i n g i t i s t h e s a m e a s i c e C o n n e c t i o n S t a t e .
* See https : //bugs.chromium.org/p/webrtc/issues/detail?id=6145#c12
* for why this is a valid hack in Chrome . In Firefox it is slightly incorrect
* since DTLS failures would be hidden . See
* https : //bugzilla.mozilla.org/show_bug.cgi?id=1265827
* for the Firefox tracking bug .
* /
function shimConnectionState ( window ) {
if ( ! window . RTCPeerConnection || 'connectionState' in window . RTCPeerConnection . prototype ) {
return ;
2017-11-28 00:22:48 +08:00
}
2019-01-26 02:03:01 +08:00
var proto = window . RTCPeerConnection . prototype ;
Object . defineProperty ( proto , 'connectionState' , {
get : function get ( ) {
return {
completed : 'connected' ,
checking : 'connecting'
} [ this . iceConnectionState ] || this . iceConnectionState ;
} ,
enumerable : true ,
configurable : true
} ) ;
Object . defineProperty ( proto , 'onconnectionstatechange' , {
get : function get ( ) {
return this . _onconnectionstatechange || null ;
} ,
set : function set ( cb ) {
if ( this . _onconnectionstatechange ) {
this . removeEventListener ( 'connectionstatechange' , this . _onconnectionstatechange ) ;
delete this . _onconnectionstatechange ;
}
if ( cb ) {
this . addEventListener ( 'connectionstatechange' , this . _onconnectionstatechange = cb ) ;
}
} ,
enumerable : true ,
configurable : true
} ) ;
[ 'setLocalDescription' , 'setRemoteDescription' ] . forEach ( function ( method ) {
var origMethod = proto [ method ] ;
proto [ method ] = function ( ) {
if ( ! this . _connectionstatechangepoly ) {
this . _connectionstatechangepoly = function ( e ) {
var pc = e . target ;
if ( pc . _lastConnectionState !== pc . connectionState ) {
pc . _lastConnectionState = pc . connectionState ;
var newEvent = new Event ( 'connectionstatechange' , e ) ;
pc . dispatchEvent ( newEvent ) ;
}
return e ;
} ;
this . addEventListener ( 'iceconnectionstatechange' , this . _connectionstatechangepoly ) ;
}
return origMethod . apply ( this , arguments ) ;
} ;
2017-11-28 00:22:48 +08:00
} ) ;
}
2019-01-26 02:03:01 +08:00
} , { "./utils" : 15 , "sdp" : 17 } ] , 7 : [ 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' ;
Object . defineProperty ( exports , "__esModule" , {
value : true
2017-11-28 00:22:48 +08:00
} ) ;
2019-01-26 02:03:01 +08:00
exports . shimGetDisplayMedia = exports . shimGetUserMedia = undefined ;
var _getusermedia = require ( './getusermedia' ) ;
Object . defineProperty ( exports , 'shimGetUserMedia' , {
enumerable : true ,
get : function get ( ) {
return _getusermedia . shimGetUserMedia ;
2017-11-28 00:22:48 +08:00
}
2019-01-26 02:03:01 +08:00
} ) ;
var _getdisplaymedia = require ( './getdisplaymedia' ) ;
Object . defineProperty ( exports , 'shimGetDisplayMedia' , {
enumerable : true ,
get : function get ( ) {
return _getdisplaymedia . shimGetDisplayMedia ;
2017-11-28 00:22:48 +08:00
}
2019-01-26 02:03:01 +08:00
} ) ;
exports . shimPeerConnection = shimPeerConnection ;
exports . shimReplaceTrack = shimReplaceTrack ;
var _utils = require ( '../utils' ) ;
var utils = _interopRequireWildcard ( _utils ) ;
var _filtericeservers = require ( './filtericeservers' ) ;
var _rtcpeerconnectionShim = require ( 'rtcpeerconnection-shim' ) ;
var _rtcpeerconnectionShim2 = _interopRequireDefault ( _rtcpeerconnectionShim ) ;
function _interopRequireDefault ( obj ) { return obj && obj . _ _esModule ? obj : { default : obj } ; }
function _interopRequireWildcard ( obj ) { if ( obj && obj . _ _esModule ) { return obj ; } else { var newObj = { } ; if ( obj != null ) { for ( var key in obj ) { if ( Object . prototype . hasOwnProperty . call ( obj , key ) ) newObj [ key ] = obj [ key ] ; } } newObj . default = obj ; return newObj ; } }
function shimPeerConnection ( window ) {
var browserDetails = utils . detectBrowser ( window ) ;
if ( window . RTCIceGatherer ) {
if ( ! window . RTCIceCandidate ) {
window . RTCIceCandidate = function ( args ) {
return args ;
2017-11-28 00:22:48 +08:00
} ;
2019-01-26 02:03:01 +08:00
}
if ( ! window . RTCSessionDescription ) {
window . RTCSessionDescription = function ( args ) {
return args ;
2017-11-28 00:22:48 +08:00
} ;
}
2019-01-26 02:03:01 +08:00
// this adds an additional event listener to MediaStrackTrack that signals
// when a tracks enabled property was changed. Workaround for a bug in
// addStream, see below. No longer required in 15025+
if ( browserDetails . version < 15025 ) {
var origMSTEnabled = Object . getOwnPropertyDescriptor ( window . MediaStreamTrack . prototype , 'enabled' ) ;
Object . defineProperty ( window . MediaStreamTrack . prototype , 'enabled' , {
set : function set ( value ) {
origMSTEnabled . set . call ( this , value ) ;
var ev = new Event ( 'enabled' ) ;
ev . enabled = value ;
this . dispatchEvent ( ev ) ;
2017-11-28 00:22:48 +08:00
}
} ) ;
2019-01-26 02:03:01 +08:00
}
}
// ORTC defines the DTMF sender a bit different.
// https://github.com/w3c/ortc/issues/714
if ( window . RTCRtpSender && ! ( 'dtmf' in window . RTCRtpSender . prototype ) ) {
2017-11-28 00:22:48 +08:00
Object . defineProperty ( window . RTCRtpSender . prototype , 'dtmf' , {
2019-01-26 02:03:01 +08:00
get : function get ( ) {
2017-11-28 00:22:48 +08:00
if ( this . _dtmf === undefined ) {
if ( this . track . kind === 'audio' ) {
2019-01-26 02:03:01 +08:00
this . _dtmf = new window . RTCDtmfSender ( this ) ;
} else if ( this . track . kind === 'video' ) {
2017-11-28 00:22:48 +08:00
this . _dtmf = null ;
}
}
return this . _dtmf ;
}
} ) ;
}
2019-01-26 02:03:01 +08:00
// Edge currently only implements the RTCDtmfSender, not the
// RTCDTMFSender alias. See http://draft.ortc.org/#rtcdtmfsender2*
if ( window . RTCDtmfSender && ! window . RTCDTMFSender ) {
window . RTCDTMFSender = window . RTCDtmfSender ;
}
var RTCPeerConnectionShim = ( 0 , _rtcpeerconnectionShim2 . default ) ( window , browserDetails . version ) ;
window . RTCPeerConnection = function ( config ) {
if ( config && config . iceServers ) {
config . iceServers = ( 0 , _filtericeservers . filterIceServers ) ( config . iceServers , browserDetails . version ) ;
utils . log ( 'ICE servers after filtering:' , config . iceServers ) ;
}
return new RTCPeerConnectionShim ( config ) ;
} ;
window . RTCPeerConnection . prototype = RTCPeerConnectionShim . prototype ;
}
function shimReplaceTrack ( window ) {
// ORTC has replaceTrack -- https://github.com/w3c/ortc/issues/614
if ( window . RTCRtpSender && ! ( 'replaceTrack' in window . RTCRtpSender . prototype ) ) {
window . RTCRtpSender . prototype . replaceTrack = window . RTCRtpSender . prototype . setTrack ;
}
}
} , { "../utils" : 15 , "./filtericeservers" : 8 , "./getdisplaymedia" : 9 , "./getusermedia" : 10 , "rtcpeerconnection-shim" : 16 } ] , 8 : [ function ( require , module , exports ) {
/ *
* Copyright ( c ) 2018 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' ;
Object . defineProperty ( exports , "__esModule" , {
value : true
} ) ;
exports . filterIceServers = filterIceServers ;
var _utils = require ( '../utils' ) ;
var utils = _interopRequireWildcard ( _utils ) ;
function _interopRequireWildcard ( obj ) { if ( obj && obj . _ _esModule ) { return obj ; } else { var newObj = { } ; if ( obj != null ) { for ( var key in obj ) { if ( Object . prototype . hasOwnProperty . call ( obj , key ) ) newObj [ key ] = obj [ key ] ; } } newObj . default = obj ; return newObj ; } }
// Edge does not like
// 1) stun: filtered after 14393 unless ?transport=udp is present
// 2) turn: that does not have all of turn:host:port?transport=udp
// 3) turn: with ipv6 addresses
// 4) turn: occurring muliple times
function filterIceServers ( iceServers , edgeVersion ) {
var hasTurn = false ;
iceServers = JSON . parse ( JSON . stringify ( iceServers ) ) ;
return iceServers . filter ( function ( server ) {
if ( server && ( server . urls || server . url ) ) {
var urls = server . urls || server . url ;
if ( server . url && ! server . urls ) {
utils . deprecated ( 'RTCIceServer.url' , 'RTCIceServer.urls' ) ;
}
var isString = typeof urls === 'string' ;
if ( isString ) {
urls = [ urls ] ;
}
urls = urls . filter ( function ( url ) {
// filter STUN unconditionally.
if ( url . indexOf ( 'stun:' ) === 0 ) {
return false ;
2017-11-28 00:22:48 +08:00
}
2019-01-26 02:03:01 +08:00
var validTurn = url . startsWith ( 'turn' ) && ! url . startsWith ( 'turn:[' ) && url . includes ( 'transport=udp' ) ;
if ( validTurn && ! hasTurn ) {
hasTurn = true ;
return true ;
}
return validTurn && ! hasTurn ;
2017-11-28 00:22:48 +08:00
} ) ;
2019-01-26 02:03:01 +08:00
delete server . url ;
server . urls = isString ? urls [ 0 ] : urls ;
return ! ! urls . length ;
2017-11-28 00:22:48 +08:00
}
2019-01-26 02:03:01 +08:00
} ) ;
}
} , { "../utils" : 15 } ] , 9 : [ function ( require , module , exports ) {
/ *
* Copyright ( c ) 2018 The adapter . js 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' ;
Object . defineProperty ( exports , "__esModule" , {
value : true
} ) ;
exports . shimGetDisplayMedia = shimGetDisplayMedia ;
function shimGetDisplayMedia ( window ) {
if ( ! ( 'getDisplayMedia' in window . navigator ) ) {
return ;
2017-11-28 00:22:48 +08:00
}
2019-01-26 02:03:01 +08:00
if ( ! window . navigator . mediaDevices ) {
2017-11-28 00:22:48 +08:00
return ;
}
2019-01-26 02:03:01 +08:00
if ( window . navigator . mediaDevices && 'getDisplayMedia' in window . navigator . mediaDevices ) {
return ;
}
window . navigator . mediaDevices . getDisplayMedia = window . navigator . getDisplayMedia . bind ( window . navigator . mediaDevices ) ;
}
} , { } ] , 10 : [ 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' ;
Object . defineProperty ( exports , "__esModule" , {
value : true
} ) ;
exports . shimGetUserMedia = shimGetUserMedia ;
function shimGetUserMedia ( window ) {
var navigator = window && window . navigator ;
var shimError _ = function shimError _ ( e ) {
return {
name : { PermissionDeniedError : 'NotAllowedError' } [ e . name ] || e . name ,
message : e . message ,
constraint : e . constraint ,
toString : function toString ( ) {
return this . name ;
2017-11-28 00:22:48 +08:00
}
2019-01-26 02:03:01 +08:00
} ;
2017-11-28 00:22:48 +08:00
} ;
2019-01-26 02:03:01 +08:00
// getUserMedia error shim.
var origGetUserMedia = navigator . mediaDevices . getUserMedia . bind ( navigator . mediaDevices ) ;
navigator . mediaDevices . getUserMedia = function ( c ) {
return origGetUserMedia ( c ) . catch ( function ( e ) {
return Promise . reject ( shimError _ ( e ) ) ;
2017-11-28 00:22:48 +08:00
} ) ;
} ;
2019-01-26 02:03:01 +08:00
}
} , { } ] , 11 : [ 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' ;
Object . defineProperty ( exports , "__esModule" , {
value : true
} ) ;
exports . shimGetDisplayMedia = exports . shimGetUserMedia = undefined ;
var _typeof = typeof Symbol === "function" && typeof Symbol . iterator === "symbol" ? function ( obj ) { return typeof obj ; } : function ( obj ) { return obj && typeof Symbol === "function" && obj . constructor === Symbol && obj !== Symbol . prototype ? "symbol" : typeof obj ; } ;
var _getusermedia = require ( './getusermedia' ) ;
Object . defineProperty ( exports , 'shimGetUserMedia' , {
enumerable : true ,
get : function get ( ) {
return _getusermedia . shimGetUserMedia ;
2017-11-28 00:22:48 +08:00
}
2019-01-26 02:03:01 +08:00
} ) ;
var _getdisplaymedia = require ( './getdisplaymedia' ) ;
Object . defineProperty ( exports , 'shimGetDisplayMedia' , {
enumerable : true ,
get : function get ( ) {
return _getdisplaymedia . shimGetDisplayMedia ;
}
} ) ;
exports . shimOnTrack = shimOnTrack ;
exports . shimPeerConnection = shimPeerConnection ;
exports . shimSenderGetStats = shimSenderGetStats ;
exports . shimReceiverGetStats = shimReceiverGetStats ;
exports . shimRemoveStream = shimRemoveStream ;
exports . shimRTCDataChannel = shimRTCDataChannel ;
var _utils = require ( '../utils' ) ;
var utils = _interopRequireWildcard ( _utils ) ;
function _interopRequireWildcard ( obj ) { if ( obj && obj . _ _esModule ) { return obj ; } else { var newObj = { } ; if ( obj != null ) { for ( var key in obj ) { if ( Object . prototype . hasOwnProperty . call ( obj , key ) ) newObj [ key ] = obj [ key ] ; } } newObj . default = obj ; return newObj ; } }
function shimOnTrack ( window ) {
if ( ( typeof window === 'undefined' ? 'undefined' : _typeof ( window ) ) === 'object' && window . RTCTrackEvent && 'receiver' in window . RTCTrackEvent . prototype && ! ( 'transceiver' in window . RTCTrackEvent . prototype ) ) {
Object . defineProperty ( window . RTCTrackEvent . prototype , 'transceiver' , {
get : function get ( ) {
return { receiver : this . receiver } ;
}
2017-11-28 00:22:48 +08:00
} ) ;
}
2019-01-26 02:03:01 +08:00
}
function shimPeerConnection ( window ) {
var browserDetails = utils . detectBrowser ( window ) ;
if ( ( typeof window === 'undefined' ? 'undefined' : _typeof ( window ) ) !== 'object' || ! ( window . RTCPeerConnection || window . mozRTCPeerConnection ) ) {
return ; // probably media.peerconnection.enabled=false in about:config
}
if ( ! window . RTCPeerConnection && window . mozRTCPeerConnection ) {
// very basic support for old versions.
window . RTCPeerConnection = window . mozRTCPeerConnection ;
}
// shim away need for obsolete RTCIceCandidate/RTCSessionDescription.
[ 'setLocalDescription' , 'setRemoteDescription' , 'addIceCandidate' ] . forEach ( function ( method ) {
2017-11-28 00:22:48 +08:00
var nativeMethod = window . RTCPeerConnection . prototype [ method ] ;
2019-01-26 02:03:01 +08:00
window . RTCPeerConnection . prototype [ method ] = function ( ) {
arguments [ 0 ] = new ( method === 'addIceCandidate' ? window . RTCIceCandidate : window . RTCSessionDescription ) ( arguments [ 0 ] ) ;
return nativeMethod . apply ( this , arguments ) ;
2017-11-28 00:22:48 +08:00
} ;
} ) ;
2019-01-26 02:03:01 +08:00
// support for addIceCandidate(null or undefined)
var nativeAddIceCandidate = window . RTCPeerConnection . prototype . addIceCandidate ;
window . RTCPeerConnection . prototype . addIceCandidate = function ( ) {
if ( ! arguments [ 0 ] ) {
if ( arguments [ 1 ] ) {
arguments [ 1 ] . apply ( null ) ;
}
return Promise . resolve ( ) ;
2017-11-28 00:22:48 +08:00
}
2019-01-26 02:03:01 +08:00
return nativeAddIceCandidate . apply ( this , arguments ) ;
2017-11-28 00:22:48 +08:00
} ;
2019-01-26 02:03:01 +08:00
var modernStatsTypes = {
inboundrtp : 'inbound-rtp' ,
outboundrtp : 'outbound-rtp' ,
candidatepair : 'candidate-pair' ,
localcandidate : 'local-candidate' ,
remotecandidate : 'remote-candidate'
} ;
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 < 53 && ! onSucc ) {
// Shim only promise getStats with spec-hyphens in type names
// Leave callback version alone; misc old uses of forEach before Map
try {
stats . forEach ( function ( stat ) {
stat . type = modernStatsTypes [ stat . type ] || stat . type ;
} ) ;
} catch ( e ) {
if ( e . name !== 'TypeError' ) {
throw e ;
2017-11-28 00:22:48 +08:00
}
2019-01-26 02:03:01 +08:00
// Avoid TypeError: "type" is read-only, in old versions. 34-43ish
stats . forEach ( function ( stat , i ) {
stats . set ( i , Object . assign ( { } , stat , {
type : modernStatsTypes [ stat . type ] || stat . type
} ) ) ;
} ) ;
2017-11-28 00:22:48 +08:00
}
}
2019-01-26 02:03:01 +08:00
return stats ;
} ) . then ( onSucc , onErr ) ;
2017-11-28 00:22:48 +08:00
} ;
2019-01-26 02:03:01 +08:00
}
function shimSenderGetStats ( window ) {
if ( ! ( ( typeof window === 'undefined' ? 'undefined' : _typeof ( window ) ) === 'object' && window . RTCPeerConnection && window . RTCRtpSender ) ) {
return ;
2017-11-28 00:22:48 +08:00
}
2019-01-26 02:03:01 +08:00
if ( window . RTCRtpSender && 'getStats' in window . RTCRtpSender . prototype ) {
return ;
}
var origGetSenders = window . RTCPeerConnection . prototype . getSenders ;
if ( origGetSenders ) {
window . RTCPeerConnection . prototype . getSenders = function ( ) {
var _this = this ;
var senders = origGetSenders . apply ( this , [ ] ) ;
senders . forEach ( function ( sender ) {
return sender . _pc = _this ;
2017-11-28 00:22:48 +08:00
} ) ;
2019-01-26 02:03:01 +08:00
return senders ;
2017-11-28 00:22:48 +08:00
} ;
2019-01-26 02:03:01 +08:00
}
var origAddTrack = window . RTCPeerConnection . prototype . addTrack ;
if ( origAddTrack ) {
window . RTCPeerConnection . prototype . addTrack = function ( ) {
var sender = origAddTrack . apply ( this , arguments ) ;
sender . _pc = this ;
return sender ;
2017-11-28 00:22:48 +08:00
} ;
2019-01-26 02:03:01 +08:00
}
window . RTCRtpSender . prototype . getStats = function ( ) {
return this . track ? this . _pc . getStats ( this . track ) : Promise . resolve ( new Map ( ) ) ;
2017-11-28 00:22:48 +08:00
} ;
2019-01-26 02:03:01 +08:00
}
function shimReceiverGetStats ( window ) {
if ( ! ( ( typeof window === 'undefined' ? 'undefined' : _typeof ( window ) ) === 'object' && window . RTCPeerConnection && window . RTCRtpSender ) ) {
return ;
2017-11-28 00:22:48 +08:00
}
2019-01-26 02:03:01 +08:00
if ( window . RTCRtpSender && 'getStats' in window . RTCRtpReceiver . prototype ) {
return ;
2017-11-28 00:22:48 +08:00
}
2019-01-26 02:03:01 +08:00
var origGetReceivers = window . RTCPeerConnection . prototype . getReceivers ;
if ( origGetReceivers ) {
window . RTCPeerConnection . prototype . getReceivers = function ( ) {
var _this2 = this ;
var receivers = origGetReceivers . apply ( this , [ ] ) ;
receivers . forEach ( function ( receiver ) {
return receiver . _pc = _this2 ;
2017-11-28 00:22:48 +08:00
} ) ;
2019-01-26 02:03:01 +08:00
return receivers ;
} ;
}
utils . wrapPeerConnectionEvent ( window , 'track' , function ( e ) {
e . receiver . _pc = e . srcElement ;
return e ;
} ) ;
window . RTCRtpReceiver . prototype . getStats = function ( ) {
return this . _pc . getStats ( this . track ) ;
} ;
}
function shimRemoveStream ( window ) {
if ( ! window . RTCPeerConnection || 'removeStream' in window . RTCPeerConnection . prototype ) {
return ;
}
window . RTCPeerConnection . prototype . removeStream = function ( stream ) {
var _this3 = this ;
utils . deprecated ( 'removeStream' , 'removeTrack' ) ;
this . getSenders ( ) . forEach ( function ( sender ) {
if ( sender . track && stream . getTracks ( ) . includes ( sender . track ) ) {
_this3 . removeTrack ( sender ) ;
2017-11-28 00:22:48 +08:00
}
2019-01-26 02:03:01 +08:00
} ) ;
2017-11-28 00:22:48 +08:00
} ;
}
2019-01-26 02:03:01 +08:00
function shimRTCDataChannel ( window ) {
// rename DataChannel to RTCDataChannel (native fix in FF60):
// https://bugzilla.mozilla.org/show_bug.cgi?id=1173851
if ( window . DataChannel && ! window . RTCDataChannel ) {
window . RTCDataChannel = window . DataChannel ;
2017-11-28 00:22:48 +08:00
}
2019-01-26 02:03:01 +08:00
}
} , { "../utils" : 15 , "./getdisplaymedia" : 12 , "./getusermedia" : 13 } ] , 12 : [ function ( require , module , exports ) {
/ *
* Copyright ( c ) 2018 The adapter . js 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' ;
Object . defineProperty ( exports , "__esModule" , {
value : true
} ) ;
exports . shimGetDisplayMedia = shimGetDisplayMedia ;
function shimGetDisplayMedia ( window , preferredMediaSource ) {
if ( window . navigator . mediaDevices && 'getDisplayMedia' in window . navigator . mediaDevices ) {
return ;
}
if ( ! window . navigator . mediaDevices ) {
return ;
}
window . navigator . mediaDevices . getDisplayMedia = function ( constraints ) {
if ( ! ( constraints && constraints . video ) ) {
var err = new DOMException ( 'getDisplayMedia without video ' + 'constraints is undefined' ) ;
err . name = 'NotFoundError' ;
// from https://heycam.github.io/webidl/#idl-DOMException-error-names
err . code = 8 ;
return Promise . reject ( err ) ;
2017-11-28 00:22:48 +08:00
}
2019-01-26 02:03:01 +08:00
if ( constraints . video === true ) {
constraints . video = { mediaSource : preferredMediaSource } ;
} else {
constraints . video . mediaSource = preferredMediaSource ;
2017-11-28 00:22:48 +08:00
}
2019-01-26 02:03:01 +08:00
return window . navigator . mediaDevices . getUserMedia ( constraints ) ;
} ;
}
} , { } ] , 13 : [ 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' ;
Object . defineProperty ( exports , "__esModule" , {
value : true
} ) ;
var _typeof = typeof Symbol === "function" && typeof Symbol . iterator === "symbol" ? function ( obj ) { return typeof obj ; } : function ( obj ) { return obj && typeof Symbol === "function" && obj . constructor === Symbol && obj !== Symbol . prototype ? "symbol" : typeof obj ; } ;
exports . shimGetUserMedia = shimGetUserMedia ;
var _utils = require ( '../utils' ) ;
var utils = _interopRequireWildcard ( _utils ) ;
function _interopRequireWildcard ( obj ) { if ( obj && obj . _ _esModule ) { return obj ; } else { var newObj = { } ; if ( obj != null ) { for ( var key in obj ) { if ( Object . prototype . hasOwnProperty . call ( obj , key ) ) newObj [ key ] = obj [ key ] ; } } newObj . default = obj ; return newObj ; } }
function shimGetUserMedia ( window ) {
var browserDetails = utils . detectBrowser ( window ) ;
var navigator = window && window . navigator ;
var MediaStreamTrack = window && window . MediaStreamTrack ;
navigator . getUserMedia = function ( constraints , onSuccess , onError ) {
// Replace Firefox 44+'s deprecation warning with unprefixed version.
utils . deprecated ( 'navigator.getUserMedia' , 'navigator.mediaDevices.getUserMedia' ) ;
navigator . mediaDevices . getUserMedia ( constraints ) . then ( onSuccess , onError ) ;
} ;
if ( ! ( browserDetails . version > 55 && 'autoGainControl' in navigator . mediaDevices . getSupportedConstraints ( ) ) ) {
var remap = function remap ( obj , a , b ) {
if ( a in obj && ! ( b in obj ) ) {
obj [ b ] = obj [ a ] ;
delete obj [ a ] ;
2017-11-28 00:22:48 +08:00
}
} ;
2019-01-26 02:03:01 +08:00
var nativeGetUserMedia = navigator . mediaDevices . getUserMedia . bind ( navigator . mediaDevices ) ;
navigator . mediaDevices . getUserMedia = function ( c ) {
if ( ( typeof c === 'undefined' ? 'undefined' : _typeof ( c ) ) === 'object' && _typeof ( c . audio ) === 'object' ) {
c = JSON . parse ( JSON . stringify ( c ) ) ;
remap ( c . audio , 'autoGainControl' , 'mozAutoGainControl' ) ;
remap ( c . audio , 'noiseSuppression' , 'mozNoiseSuppression' ) ;
2017-11-28 00:22:48 +08:00
}
2019-01-26 02:03:01 +08:00
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 ;
} ;
2017-11-28 00:22:48 +08:00
}
2019-01-26 02:03:01 +08:00
if ( MediaStreamTrack && MediaStreamTrack . prototype . applyConstraints ) {
var nativeApplyConstraints = MediaStreamTrack . prototype . applyConstraints ;
MediaStreamTrack . prototype . applyConstraints = function ( c ) {
if ( this . kind === 'audio' && ( typeof c === 'undefined' ? 'undefined' : _typeof ( c ) ) === 'object' ) {
c = JSON . parse ( JSON . stringify ( c ) ) ;
remap ( c , 'autoGainControl' , 'mozAutoGainControl' ) ;
remap ( c , 'noiseSuppression' , 'mozNoiseSuppression' ) ;
2017-11-28 00:22:48 +08:00
}
2019-01-26 02:03:01 +08:00
return nativeApplyConstraints . apply ( this , [ c ] ) ;
} ;
2017-11-28 00:22:48 +08:00
}
}
2019-01-26 02:03:01 +08:00
}
} , { "../utils" : 15 } ] , 14 : [ 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 .
* /
'use strict' ;
Object . defineProperty ( exports , "__esModule" , {
value : true
} ) ;
var _typeof = typeof Symbol === "function" && typeof Symbol . iterator === "symbol" ? function ( obj ) { return typeof obj ; } : function ( obj ) { return obj && typeof Symbol === "function" && obj . constructor === Symbol && obj !== Symbol . prototype ? "symbol" : typeof obj ; } ;
exports . shimLocalStreamsAPI = shimLocalStreamsAPI ;
exports . shimRemoteStreamsAPI = shimRemoteStreamsAPI ;
exports . shimCallbacksAPI = shimCallbacksAPI ;
exports . shimGetUserMedia = shimGetUserMedia ;
exports . shimRTCIceServerUrls = shimRTCIceServerUrls ;
exports . shimTrackEventTransceiver = shimTrackEventTransceiver ;
exports . shimCreateOfferLegacy = shimCreateOfferLegacy ;
var _utils = require ( '../utils' ) ;
var utils = _interopRequireWildcard ( _utils ) ;
function _interopRequireWildcard ( obj ) { if ( obj && obj . _ _esModule ) { return obj ; } else { var newObj = { } ; if ( obj != null ) { for ( var key in obj ) { if ( Object . prototype . hasOwnProperty . call ( obj , key ) ) newObj [ key ] = obj [ key ] ; } } newObj . default = obj ; return newObj ; } }
function shimLocalStreamsAPI ( window ) {
if ( ( typeof window === 'undefined' ? 'undefined' : _typeof ( window ) ) !== 'object' || ! window . RTCPeerConnection ) {
return ;
}
if ( ! ( 'getLocalStreams' in window . RTCPeerConnection . prototype ) ) {
window . RTCPeerConnection . prototype . getLocalStreams = function ( ) {
if ( ! this . _localStreams ) {
this . _localStreams = [ ] ;
2017-11-28 00:22:48 +08:00
}
2019-01-26 02:03:01 +08:00
return this . _localStreams ;
2017-11-28 00:22:48 +08:00
} ;
}
2019-01-26 02:03:01 +08:00
if ( ! ( 'addStream' in window . RTCPeerConnection . prototype ) ) {
var _addTrack = window . RTCPeerConnection . prototype . addTrack ;
window . RTCPeerConnection . prototype . addStream = function ( stream ) {
var _this = this ;
if ( ! this . _localStreams ) {
this . _localStreams = [ ] ;
2017-11-28 00:22:48 +08:00
}
2019-01-26 02:03:01 +08:00
if ( ! this . _localStreams . includes ( stream ) ) {
this . _localStreams . push ( stream ) ;
2017-11-28 00:22:48 +08:00
}
2019-01-26 02:03:01 +08:00
stream . getTracks ( ) . forEach ( function ( track ) {
return _addTrack . call ( _this , track , stream ) ;
} ) ;
} ;
window . RTCPeerConnection . prototype . addTrack = function ( track , stream ) {
if ( stream ) {
if ( ! this . _localStreams ) {
this . _localStreams = [ stream ] ;
} else if ( ! this . _localStreams . includes ( stream ) ) {
this . _localStreams . push ( stream ) ;
}
}
return _addTrack . call ( this , track , stream ) ;
} ;
2017-11-28 00:22:48 +08:00
}
2019-01-26 02:03:01 +08:00
if ( ! ( 'removeStream' in window . RTCPeerConnection . prototype ) ) {
window . RTCPeerConnection . prototype . removeStream = function ( stream ) {
var _this2 = this ;
if ( ! this . _localStreams ) {
this . _localStreams = [ ] ;
2017-11-28 00:22:48 +08:00
}
2019-01-26 02:03:01 +08:00
var index = this . _localStreams . indexOf ( stream ) ;
if ( index === - 1 ) {
return ;
}
this . _localStreams . splice ( index , 1 ) ;
var tracks = stream . getTracks ( ) ;
this . getSenders ( ) . forEach ( function ( sender ) {
if ( tracks . includes ( sender . track ) ) {
_this2 . removeTrack ( sender ) ;
2017-11-28 00:22:48 +08:00
}
} ) ;
2019-01-26 02:03:01 +08:00
} ;
}
2017-11-28 00:22:48 +08:00
}
2019-01-26 02:03:01 +08:00
function shimRemoteStreamsAPI ( window ) {
if ( ( typeof window === 'undefined' ? 'undefined' : _typeof ( window ) ) !== 'object' || ! window . RTCPeerConnection ) {
return ;
2017-11-28 00:22:48 +08:00
}
2019-01-26 02:03:01 +08:00
if ( ! ( 'getRemoteStreams' in window . RTCPeerConnection . prototype ) ) {
window . RTCPeerConnection . prototype . getRemoteStreams = function ( ) {
return this . _remoteStreams ? this . _remoteStreams : [ ] ;
} ;
2017-11-28 00:22:48 +08:00
}
2019-01-26 02:03:01 +08:00
if ( ! ( 'onaddstream' in window . RTCPeerConnection . prototype ) ) {
Object . defineProperty ( window . RTCPeerConnection . prototype , 'onaddstream' , {
get : function get ( ) {
return this . _onaddstream ;
} ,
set : function set ( f ) {
var _this3 = this ;
if ( this . _onaddstream ) {
this . removeEventListener ( 'addstream' , this . _onaddstream ) ;
this . removeEventListener ( 'track' , this . _onaddstreampoly ) ;
}
this . addEventListener ( 'addstream' , this . _onaddstream = f ) ;
this . addEventListener ( 'track' , this . _onaddstreampoly = function ( e ) {
e . streams . forEach ( function ( stream ) {
if ( ! _this3 . _remoteStreams ) {
_this3 . _remoteStreams = [ ] ;
}
if ( _this3 . _remoteStreams . includes ( stream ) ) {
return ;
}
_this3 . _remoteStreams . push ( stream ) ;
var event = new Event ( 'addstream' ) ;
event . stream = stream ;
_this3 . dispatchEvent ( event ) ;
} ) ;
} ) ;
}
} ) ;
var origSetRemoteDescription = window . RTCPeerConnection . prototype . setRemoteDescription ;
window . RTCPeerConnection . prototype . setRemoteDescription = function ( ) {
var pc = this ;
if ( ! this . _onaddstreampoly ) {
this . addEventListener ( 'track' , this . _onaddstreampoly = function ( e ) {
e . streams . forEach ( function ( stream ) {
if ( ! pc . _remoteStreams ) {
pc . _remoteStreams = [ ] ;
}
if ( pc . _remoteStreams . indexOf ( stream ) >= 0 ) {
return ;
}
pc . _remoteStreams . push ( stream ) ;
var event = new Event ( 'addstream' ) ;
event . stream = stream ;
pc . dispatchEvent ( event ) ;
} ) ;
} ) ;
}
return origSetRemoteDescription . apply ( pc , arguments ) ;
} ;
2017-11-28 00:22:48 +08:00
}
2019-01-26 02:03:01 +08:00
}
function shimCallbacksAPI ( window ) {
if ( ( typeof window === 'undefined' ? 'undefined' : _typeof ( window ) ) !== 'object' || ! window . RTCPeerConnection ) {
2017-11-28 00:22:48 +08:00
return ;
}
2019-01-26 02:03:01 +08:00
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 ;
2017-11-28 00:22:48 +08:00
}
2019-01-26 02:03:01 +08:00
promise . then ( successCallback , failureCallback ) ;
return Promise . resolve ( ) ;
2017-11-28 00:22:48 +08:00
} ;
2019-01-26 02:03:01 +08:00
prototype . createAnswer = function ( successCallback , failureCallback ) {
var options = arguments . length >= 2 ? arguments [ 2 ] : arguments [ 0 ] ;
var promise = createAnswer . apply ( this , [ options ] ) ;
if ( ! failureCallback ) {
return promise ;
2017-11-28 00:22:48 +08:00
}
2019-01-26 02:03:01 +08:00
promise . then ( successCallback , failureCallback ) ;
return Promise . resolve ( ) ;
2017-11-28 00:22:48 +08:00
} ;
2019-01-26 02:03:01 +08:00
var withCallback = function withCallback ( description , successCallback , failureCallback ) {
var promise = setLocalDescription . apply ( this , [ description ] ) ;
if ( ! failureCallback ) {
return promise ;
}
promise . then ( successCallback , failureCallback ) ;
return Promise . resolve ( ) ;
2017-11-28 00:22:48 +08:00
} ;
2019-01-26 02:03:01 +08:00
prototype . setLocalDescription = withCallback ;
withCallback = function withCallback ( description , successCallback , failureCallback ) {
var promise = setRemoteDescription . apply ( this , [ description ] ) ;
if ( ! failureCallback ) {
return promise ;
2017-11-28 00:22:48 +08:00
}
2019-01-26 02:03:01 +08:00
promise . then ( successCallback , failureCallback ) ;
return Promise . resolve ( ) ;
} ;
prototype . setRemoteDescription = withCallback ;
withCallback = function withCallback ( candidate , successCallback , failureCallback ) {
var promise = addIceCandidate . apply ( this , [ candidate ] ) ;
if ( ! failureCallback ) {
return promise ;
2017-11-28 00:22:48 +08:00
}
2019-01-26 02:03:01 +08:00
promise . then ( successCallback , failureCallback ) ;
return Promise . resolve ( ) ;
2017-11-28 00:22:48 +08:00
} ;
2019-01-26 02:03:01 +08:00
prototype . addIceCandidate = withCallback ;
2017-11-28 00:22:48 +08:00
}
2019-01-26 02:03:01 +08:00
function shimGetUserMedia ( window ) {
var navigator = window && window . navigator ;
if ( ! navigator . getUserMedia && navigator . mediaDevices && navigator . mediaDevices . getUserMedia ) {
navigator . getUserMedia = function ( constraints , cb , errcb ) {
navigator . mediaDevices . getUserMedia ( constraints ) . then ( cb , errcb ) ;
} . bind ( navigator ) ;
2017-11-28 00:22:48 +08:00
}
2019-01-26 02:03:01 +08:00
}
function shimRTCIceServerUrls ( window ) {
// migrate from non-spec RTCIceServer.url to RTCIceServer.urls
var OrigPeerConnection = window . RTCPeerConnection ;
window . RTCPeerConnection = function ( pcConfig , pcConstraints ) {
if ( pcConfig && pcConfig . iceServers ) {
var newIceServers = [ ] ;
for ( var i = 0 ; i < pcConfig . iceServers . length ; i ++ ) {
var server = pcConfig . iceServers [ i ] ;
if ( ! server . hasOwnProperty ( 'urls' ) && server . hasOwnProperty ( 'url' ) ) {
utils . deprecated ( 'RTCIceServer.url' , 'RTCIceServer.urls' ) ;
server = JSON . parse ( JSON . stringify ( server ) ) ;
server . urls = server . url ;
delete server . url ;
newIceServers . push ( server ) ;
} else {
newIceServers . push ( pcConfig . iceServers [ i ] ) ;
2017-11-28 00:22:48 +08:00
}
}
2019-01-26 02:03:01 +08:00
pcConfig . iceServers = newIceServers ;
2017-11-28 00:22:48 +08:00
}
2019-01-26 02:03:01 +08:00
return new OrigPeerConnection ( pcConfig , pcConstraints ) ;
2017-11-28 00:22:48 +08:00
} ;
2019-01-26 02:03:01 +08:00
window . RTCPeerConnection . prototype = OrigPeerConnection . prototype ;
// wrap static methods. Currently just generateCertificate.
if ( 'generateCertificate' in window . RTCPeerConnection ) {
Object . defineProperty ( window . RTCPeerConnection , 'generateCertificate' , {
get : function get ( ) {
return OrigPeerConnection . generateCertificate ;
2017-11-28 00:22:48 +08:00
}
} ) ;
}
2019-01-26 02:03:01 +08:00
}
function shimTrackEventTransceiver ( window ) {
// Add event.transceiver member over deprecated event.receiver
if ( ( typeof window === 'undefined' ? 'undefined' : _typeof ( window ) ) === 'object' && window . RTCPeerConnection && 'receiver' in window . RTCTrackEvent . prototype &&
// can't check 'transceiver' in window.RTCTrackEvent.prototype, as it is
// defined for some reason even when window.RTCTransceiver is not.
! window . RTCTransceiver ) {
2017-11-28 00:22:48 +08:00
Object . defineProperty ( window . RTCTrackEvent . prototype , 'transceiver' , {
2019-01-26 02:03:01 +08:00
get : function get ( ) {
return { receiver : this . receiver } ;
2017-11-28 00:22:48 +08:00
}
} ) ;
}
2019-01-26 02:03:01 +08:00
}
function shimCreateOfferLegacy ( window ) {
var origCreateOffer = window . RTCPeerConnection . prototype . createOffer ;
window . RTCPeerConnection . prototype . createOffer = function ( offerOptions ) {
if ( offerOptions ) {
if ( typeof offerOptions . offerToReceiveAudio !== 'undefined' ) {
// support bit values
offerOptions . offerToReceiveAudio = ! ! offerOptions . offerToReceiveAudio ;
2017-11-28 00:22:48 +08:00
}
2019-01-26 02:03:01 +08:00
var audioTransceiver = this . getTransceivers ( ) . find ( function ( transceiver ) {
return transceiver . sender . track && transceiver . sender . track . kind === 'audio' ;
2017-11-28 00:22:48 +08:00
} ) ;
2019-01-26 02:03:01 +08:00
if ( offerOptions . offerToReceiveAudio === false && audioTransceiver ) {
if ( audioTransceiver . direction === 'sendrecv' ) {
if ( audioTransceiver . setDirection ) {
audioTransceiver . setDirection ( 'sendonly' ) ;
} else {
audioTransceiver . direction = 'sendonly' ;
}
} else if ( audioTransceiver . direction === 'recvonly' ) {
if ( audioTransceiver . setDirection ) {
audioTransceiver . setDirection ( 'inactive' ) ;
} else {
audioTransceiver . direction = 'inactive' ;
2017-11-28 00:22:48 +08:00
}
}
2019-01-26 02:03:01 +08:00
} else if ( offerOptions . offerToReceiveAudio === true && ! audioTransceiver ) {
this . addTransceiver ( 'audio' ) ;
2017-11-28 00:22:48 +08:00
}
2019-01-26 02:03:01 +08:00
if ( typeof offerOptions . offerToReceiveVideo !== 'undefined' ) {
// support bit values
offerOptions . offerToReceiveVideo = ! ! offerOptions . offerToReceiveVideo ;
2017-11-28 00:22:48 +08:00
}
2019-01-26 02:03:01 +08:00
var videoTransceiver = this . getTransceivers ( ) . find ( function ( transceiver ) {
return transceiver . sender . track && transceiver . sender . track . kind === 'video' ;
} ) ;
if ( offerOptions . offerToReceiveVideo === false && videoTransceiver ) {
if ( videoTransceiver . direction === 'sendrecv' ) {
if ( videoTransceiver . setDirection ) {
videoTransceiver . setDirection ( 'sendonly' ) ;
} else {
videoTransceiver . direction = 'sendonly' ;
}
} else if ( videoTransceiver . direction === 'recvonly' ) {
if ( videoTransceiver . setDirection ) {
videoTransceiver . setDirection ( 'inactive' ) ;
} else {
videoTransceiver . direction = 'inactive' ;
}
2017-11-28 00:22:48 +08:00
}
2019-01-26 02:03:01 +08:00
} else if ( offerOptions . offerToReceiveVideo === true && ! videoTransceiver ) {
this . addTransceiver ( 'video' ) ;
2017-11-28 00:22:48 +08:00
}
}
2019-01-26 02:03:01 +08:00
return origCreateOffer . apply ( this , arguments ) ;
2017-11-28 00:22:48 +08:00
} ;
}
2019-01-26 02:03:01 +08:00
} , { "../utils" : 15 } ] , 15 : [ 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' ;
Object . defineProperty ( exports , "__esModule" , {
value : true
} ) ;
var _typeof = typeof Symbol === "function" && typeof Symbol . iterator === "symbol" ? function ( obj ) { return typeof obj ; } : function ( obj ) { return obj && typeof Symbol === "function" && obj . constructor === Symbol && obj !== Symbol . prototype ? "symbol" : typeof obj ; } ;
exports . extractVersion = extractVersion ;
exports . wrapPeerConnectionEvent = wrapPeerConnectionEvent ;
exports . disableLog = disableLog ;
exports . disableWarnings = disableWarnings ;
exports . log = log ;
exports . deprecated = deprecated ;
exports . detectBrowser = detectBrowser ;
var logDisabled _ = true ;
var deprecationWarnings _ = true ;
/ * *
* Extract browser version out of the provided user agent string .
*
* @ param { ! string } uastring userAgent string .
* @ param { ! string } expr Regular expression used as match criteria .
* @ param { ! number } pos position in the version string to be returned .
* @ return { ! number } browser version .
* /
function extractVersion ( uastring , expr , pos ) {
var match = uastring . match ( expr ) ;
return match && match . length >= pos && parseInt ( match [ pos ] , 10 ) ;
2017-11-28 00:22:48 +08:00
}
2019-01-26 02:03:01 +08:00
// Wraps the peerconnection event eventNameToWrap in a function
// which returns the modified event object (or false to prevent
// the event).
function wrapPeerConnectionEvent ( window , eventNameToWrap , wrapper ) {
if ( ! window . RTCPeerConnection ) {
return ;
}
var proto = window . RTCPeerConnection . prototype ;
var nativeAddEventListener = proto . addEventListener ;
proto . addEventListener = function ( nativeEventName , cb ) {
if ( nativeEventName !== eventNameToWrap ) {
return nativeAddEventListener . apply ( this , arguments ) ;
2017-11-28 00:22:48 +08:00
}
2019-01-26 02:03:01 +08:00
var wrappedCallback = function wrappedCallback ( e ) {
var modifiedEvent = wrapper ( e ) ;
if ( modifiedEvent ) {
cb ( modifiedEvent ) ;
}
} ;
this . _eventMap = this . _eventMap || { } ;
this . _eventMap [ cb ] = wrappedCallback ;
return nativeAddEventListener . apply ( this , [ nativeEventName , wrappedCallback ] ) ;
2017-11-28 00:22:48 +08:00
} ;
2019-01-26 02:03:01 +08:00
var nativeRemoveEventListener = proto . removeEventListener ;
proto . removeEventListener = function ( nativeEventName , cb ) {
if ( nativeEventName !== eventNameToWrap || ! this . _eventMap || ! this . _eventMap [ cb ] ) {
return nativeRemoveEventListener . apply ( this , arguments ) ;
2017-11-28 00:22:48 +08:00
}
2019-01-26 02:03:01 +08:00
var unwrappedCb = this . _eventMap [ cb ] ;
delete this . _eventMap [ cb ] ;
return nativeRemoveEventListener . apply ( this , [ nativeEventName , unwrappedCb ] ) ;
2017-11-28 00:22:48 +08:00
} ;
2019-01-26 02:03:01 +08:00
Object . defineProperty ( proto , 'on' + eventNameToWrap , {
get : function get ( ) {
return this [ '_on' + eventNameToWrap ] ;
} ,
set : function set ( cb ) {
if ( this [ '_on' + eventNameToWrap ] ) {
this . removeEventListener ( eventNameToWrap , this [ '_on' + eventNameToWrap ] ) ;
delete this [ '_on' + eventNameToWrap ] ;
2017-11-28 00:22:48 +08:00
}
2019-01-26 02:03:01 +08:00
if ( cb ) {
this . addEventListener ( eventNameToWrap , this [ '_on' + eventNameToWrap ] = cb ) ;
2017-11-28 00:22:48 +08:00
}
2019-01-26 02:03:01 +08:00
} ,
enumerable : true ,
configurable : true
} ) ;
2017-11-28 00:22:48 +08:00
}
2019-01-26 02:03:01 +08:00
function disableLog ( bool ) {
2017-11-28 00:22:48 +08:00
if ( typeof bool !== 'boolean' ) {
2019-01-26 02:03:01 +08:00
return new Error ( 'Argument type: ' + ( typeof bool === 'undefined' ? 'undefined' : _typeof ( bool ) ) + '. Please use a boolean.' ) ;
2017-11-28 00:22:48 +08:00
}
logDisabled _ = bool ;
2019-01-26 02:03:01 +08:00
return bool ? 'adapter.js logging disabled' : 'adapter.js logging enabled' ;
}
2017-11-28 00:22:48 +08:00
/ * *
* Disable or enable deprecation warnings
* @ param { ! boolean } bool set to true to disable warnings .
* /
2019-01-26 02:03:01 +08:00
function disableWarnings ( bool ) {
2017-11-28 00:22:48 +08:00
if ( typeof bool !== 'boolean' ) {
2019-01-26 02:03:01 +08:00
return new Error ( 'Argument type: ' + ( typeof bool === 'undefined' ? 'undefined' : _typeof ( bool ) ) + '. Please use a boolean.' ) ;
2017-11-28 00:22:48 +08:00
}
deprecationWarnings _ = ! bool ;
return 'adapter.js deprecation warnings ' + ( bool ? 'disabled' : 'enabled' ) ;
2019-01-26 02:03:01 +08:00
}
function log ( ) {
if ( ( typeof window === 'undefined' ? 'undefined' : _typeof ( window ) ) === 'object' ) {
2017-11-28 00:22:48 +08:00
if ( logDisabled _ ) {
return ;
}
if ( typeof console !== 'undefined' && typeof console . log === 'function' ) {
console . log . apply ( console , arguments ) ;
}
}
2019-01-26 02:03:01 +08:00
}
2017-11-28 00:22:48 +08:00
/ * *
* Shows a deprecation warning suggesting the modern and spec - compatible API .
* /
2019-01-26 02:03:01 +08:00
function deprecated ( oldMethod , newMethod ) {
2017-11-28 00:22:48 +08:00
if ( ! deprecationWarnings _ ) {
return ;
}
2019-01-26 02:03:01 +08:00
console . warn ( oldMethod + ' is deprecated, please use ' + newMethod + ' instead.' ) ;
}
2017-11-28 00:22:48 +08:00
/ * *
* Browser detector .
*
* @ return { object } result containing browser and version
* properties .
* /
2019-01-26 02:03:01 +08:00
function detectBrowser ( window ) {
var navigator = window . navigator ;
2017-11-28 00:22:48 +08:00
// Returned result object.
2019-01-26 02:03:01 +08:00
var result = { browser : null , version : null } ;
2017-11-28 00:22:48 +08:00
// Fail early if it's not a browser
if ( typeof window === 'undefined' || ! window . navigator ) {
result . browser = 'Not a browser.' ;
return result ;
}
2019-01-26 02:03:01 +08:00
2017-11-28 00:22:48 +08:00
if ( navigator . mozGetUserMedia ) {
2019-01-26 02:03:01 +08:00
// Firefox.
2017-11-28 00:22:48 +08:00
result . browser = 'firefox' ;
2019-01-26 02:03:01 +08:00
result . version = extractVersion ( navigator . userAgent , /Firefox\/(\d+)\./ , 1 ) ;
2017-11-28 00:22:48 +08:00
} else if ( navigator . webkitGetUserMedia ) {
2019-01-26 02:03:01 +08:00
// Chrome, Chromium, Webview, Opera.
// Version matches Chrome/WebRTC version.
result . browser = 'chrome' ;
result . version = extractVersion ( navigator . userAgent , /Chrom(e|ium)\/(\d+)\./ , 2 ) ;
} else if ( navigator . mediaDevices && navigator . userAgent . match ( /Edge\/(\d+).(\d+)$/ ) ) {
// Edge.
2017-11-28 00:22:48 +08:00
result . browser = 'edge' ;
2019-01-26 02:03:01 +08:00
result . version = extractVersion ( navigator . userAgent , /Edge\/(\d+).(\d+)$/ , 2 ) ;
} else if ( window . RTCPeerConnection && navigator . userAgent . match ( /AppleWebKit\/(\d+)\./ ) ) {
// Safari.
2017-11-28 00:22:48 +08:00
result . browser = 'safari' ;
2019-01-26 02:03:01 +08:00
result . version = extractVersion ( navigator . userAgent , /AppleWebKit\/(\d+)\./ , 1 ) ;
} else {
// Default fallthrough: not supported.
2017-11-28 00:22:48 +08:00
result . browser = 'Not a supported browser.' ;
return result ;
}
2019-01-26 02:03:01 +08:00
2017-11-28 00:22:48 +08:00
return result ;
2019-01-26 02:03:01 +08:00
}
} , { } ] , 16 : [ function ( require , module , exports ) {
/ *
* Copyright ( c ) 2017 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 SDPUtils = require ( 'sdp' ) ;
function fixStatsType ( stat ) {
return {
inboundrtp : 'inbound-rtp' ,
outboundrtp : 'outbound-rtp' ,
candidatepair : 'candidate-pair' ,
localcandidate : 'local-candidate' ,
remotecandidate : 'remote-candidate'
} [ stat . type ] || stat . type ;
}
function writeMediaSection ( transceiver , caps , type , stream , dtlsRole ) {
var sdp = SDPUtils . writeRtpDescription ( transceiver . kind , caps ) ;
// Map ICE parameters (ufrag, pwd) to SDP.
sdp += SDPUtils . writeIceParameters (
transceiver . iceGatherer . getLocalParameters ( ) ) ;
// Map DTLS parameters to SDP.
sdp += SDPUtils . writeDtlsParameters (
transceiver . dtlsTransport . getLocalParameters ( ) ,
type === 'offer' ? 'actpass' : dtlsRole || 'active' ) ;
sdp += 'a=mid:' + transceiver . mid + '\r\n' ;
if ( transceiver . rtpSender && transceiver . rtpReceiver ) {
sdp += 'a=sendrecv\r\n' ;
} else if ( transceiver . rtpSender ) {
sdp += 'a=sendonly\r\n' ;
} else if ( transceiver . rtpReceiver ) {
sdp += 'a=recvonly\r\n' ;
} else {
sdp += 'a=inactive\r\n' ;
}
if ( transceiver . rtpSender ) {
var trackId = transceiver . rtpSender . _initialTrackId ||
transceiver . rtpSender . track . id ;
transceiver . rtpSender . _initialTrackId = trackId ;
// spec.
var msid = 'msid:' + ( stream ? stream . id : '-' ) + ' ' +
trackId + '\r\n' ;
sdp += 'a=' + msid ;
// for Chrome. Legacy should no longer be required.
sdp += 'a=ssrc:' + transceiver . sendEncodingParameters [ 0 ] . ssrc +
' ' + msid ;
// RTX
if ( transceiver . sendEncodingParameters [ 0 ] . rtx ) {
sdp += 'a=ssrc:' + transceiver . sendEncodingParameters [ 0 ] . rtx . ssrc +
' ' + msid ;
sdp += 'a=ssrc-group:FID ' +
transceiver . sendEncodingParameters [ 0 ] . ssrc + ' ' +
transceiver . sendEncodingParameters [ 0 ] . rtx . ssrc +
'\r\n' ;
}
}
// FIXME: this should be written by writeRtpDescription.
sdp += 'a=ssrc:' + transceiver . sendEncodingParameters [ 0 ] . ssrc +
' cname:' + SDPUtils . localCName + '\r\n' ;
if ( transceiver . rtpSender && transceiver . sendEncodingParameters [ 0 ] . rtx ) {
sdp += 'a=ssrc:' + transceiver . sendEncodingParameters [ 0 ] . rtx . ssrc +
' cname:' + SDPUtils . localCName + '\r\n' ;
}
return sdp ;
}
// Edge does not like
// 1) stun: filtered after 14393 unless ?transport=udp is present
// 2) turn: that does not have all of turn:host:port?transport=udp
// 3) turn: with ipv6 addresses
// 4) turn: occurring muliple times
function filterIceServers ( iceServers , edgeVersion ) {
var hasTurn = false ;
iceServers = JSON . parse ( JSON . stringify ( iceServers ) ) ;
return iceServers . filter ( function ( server ) {
if ( server && ( server . urls || server . url ) ) {
var urls = server . urls || server . url ;
if ( server . url && ! server . urls ) {
console . warn ( 'RTCIceServer.url is deprecated! Use urls instead.' ) ;
}
var isString = typeof urls === 'string' ;
if ( isString ) {
urls = [ urls ] ;
}
urls = urls . filter ( function ( url ) {
var validTurn = url . indexOf ( 'turn:' ) === 0 &&
url . indexOf ( 'transport=udp' ) !== - 1 &&
url . indexOf ( 'turn:[' ) === - 1 &&
! hasTurn ;
if ( validTurn ) {
hasTurn = true ;
return true ;
}
return url . indexOf ( 'stun:' ) === 0 && edgeVersion >= 14393 &&
url . indexOf ( '?transport=udp' ) === - 1 ;
} ) ;
delete server . url ;
server . urls = isString ? urls [ 0 ] : urls ;
return ! ! urls . length ;
}
} ) ;
}
// 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 ;
}
function maybeAddCandidate ( iceTransport , candidate ) {
// Edge's internal representation adds some fields therefore
// not all fieldѕ are taken into account.
var alreadyAdded = iceTransport . getRemoteCandidates ( )
. find ( function ( remoteCandidate ) {
return candidate . foundation === remoteCandidate . foundation &&
candidate . ip === remoteCandidate . ip &&
candidate . port === remoteCandidate . port &&
candidate . priority === remoteCandidate . priority &&
candidate . protocol === remoteCandidate . protocol &&
candidate . type === remoteCandidate . type ;
} ) ;
if ( ! alreadyAdded ) {
iceTransport . addRemoteCandidate ( candidate ) ;
}
return ! alreadyAdded ;
}
function makeError ( name , description ) {
var e = new Error ( description ) ;
e . name = name ;
// legacy error codes from https://heycam.github.io/webidl/#idl-DOMException-error-names
e . code = {
NotSupportedError : 9 ,
InvalidStateError : 11 ,
InvalidAccessError : 15 ,
TypeError : undefined ,
OperationError : undefined
} [ name ] ;
return e ;
}
module . exports = function ( window , edgeVersion ) {
// https://w3c.github.io/mediacapture-main/#mediastream
// Helper function to add the track to the stream and
// dispatch the event ourselves.
function addTrackToStreamAndFireEvent ( track , stream ) {
stream . addTrack ( track ) ;
stream . dispatchEvent ( new window . MediaStreamTrackEvent ( 'addtrack' ,
{ track : track } ) ) ;
}
function removeTrackFromStreamAndFireEvent ( track , stream ) {
stream . removeTrack ( track ) ;
stream . dispatchEvent ( new window . MediaStreamTrackEvent ( 'removetrack' ,
{ track : track } ) ) ;
}
function fireAddTrack ( pc , track , receiver , streams ) {
var trackEvent = new Event ( 'track' ) ;
trackEvent . track = track ;
trackEvent . receiver = receiver ;
trackEvent . transceiver = { receiver : receiver } ;
trackEvent . streams = streams ;
window . setTimeout ( function ( ) {
pc . _dispatchEvent ( 'track' , trackEvent ) ;
} ) ;
}
var RTCPeerConnection = function ( config ) {
var pc = this ;
var _eventTarget = document . createDocumentFragment ( ) ;
[ 'addEventListener' , 'removeEventListener' , 'dispatchEvent' ]
. forEach ( function ( method ) {
pc [ method ] = _eventTarget [ method ] . bind ( _eventTarget ) ;
} ) ;
this . canTrickleIceCandidates = null ;
this . needNegotiation = false ;
this . localStreams = [ ] ;
this . remoteStreams = [ ] ;
this . _localDescription = null ;
this . _remoteDescription = null ;
this . signalingState = 'stable' ;
this . iceConnectionState = 'new' ;
this . connectionState = 'new' ;
this . iceGatheringState = 'new' ;
config = JSON . parse ( JSON . stringify ( config || { } ) ) ;
this . usingBundle = config . bundlePolicy === 'max-bundle' ;
if ( config . rtcpMuxPolicy === 'negotiate' ) {
throw ( makeError ( 'NotSupportedError' ,
'rtcpMuxPolicy \'negotiate\' is not supported' ) ) ;
} else if ( ! config . rtcpMuxPolicy ) {
config . rtcpMuxPolicy = 'require' ;
}
switch ( config . iceTransportPolicy ) {
case 'all' :
case 'relay' :
break ;
default :
config . iceTransportPolicy = 'all' ;
break ;
}
switch ( config . bundlePolicy ) {
case 'balanced' :
case 'max-compat' :
case 'max-bundle' :
break ;
default :
config . bundlePolicy = 'balanced' ;
break ;
}
config . iceServers = filterIceServers ( config . iceServers || [ ] , edgeVersion ) ;
this . _iceGatherers = [ ] ;
if ( config . iceCandidatePoolSize ) {
for ( var i = config . iceCandidatePoolSize ; i > 0 ; i -- ) {
this . _iceGatherers . push ( new window . RTCIceGatherer ( {
iceServers : config . iceServers ,
gatherPolicy : config . iceTransportPolicy
} ) ) ;
}
} else {
config . iceCandidatePoolSize = 0 ;
}
this . _config = config ;
// per-track iceGathers, iceTransports, dtlsTransports, rtpSenders, ...
// everything that is needed to describe a SDP m-line.
this . transceivers = [ ] ;
this . _sdpSessionId = SDPUtils . generateSessionId ( ) ;
this . _sdpSessionVersion = 0 ;
this . _dtlsRole = undefined ; // role for a=setup to use in answers.
this . _isClosed = false ;
} ;
Object . defineProperty ( RTCPeerConnection . prototype , 'localDescription' , {
configurable : true ,
get : function ( ) {
return this . _localDescription ;
}
} ) ;
Object . defineProperty ( RTCPeerConnection . prototype , 'remoteDescription' , {
configurable : true ,
get : function ( ) {
return this . _remoteDescription ;
}
} ) ;
// set up event handlers on prototype
RTCPeerConnection . prototype . onicecandidate = null ;
RTCPeerConnection . prototype . onaddstream = null ;
RTCPeerConnection . prototype . ontrack = null ;
RTCPeerConnection . prototype . onremovestream = null ;
RTCPeerConnection . prototype . onsignalingstatechange = null ;
RTCPeerConnection . prototype . oniceconnectionstatechange = null ;
RTCPeerConnection . prototype . onconnectionstatechange = null ;
RTCPeerConnection . prototype . onicegatheringstatechange = null ;
RTCPeerConnection . prototype . onnegotiationneeded = null ;
RTCPeerConnection . prototype . ondatachannel = null ;
RTCPeerConnection . prototype . _dispatchEvent = function ( name , event ) {
if ( this . _isClosed ) {
return ;
}
this . dispatchEvent ( event ) ;
if ( typeof this [ 'on' + name ] === 'function' ) {
this [ 'on' + name ] ( event ) ;
}
} ;
RTCPeerConnection . prototype . _emitGatheringStateChange = function ( ) {
var event = new Event ( 'icegatheringstatechange' ) ;
this . _dispatchEvent ( 'icegatheringstatechange' , event ) ;
} ;
RTCPeerConnection . prototype . getConfiguration = function ( ) {
return this . _config ;
} ;
RTCPeerConnection . prototype . getLocalStreams = function ( ) {
return this . localStreams ;
} ;
RTCPeerConnection . prototype . getRemoteStreams = function ( ) {
return this . remoteStreams ;
} ;
// internal helper to create a transceiver object.
// (which is not yet the same as the WebRTC 1.0 transceiver)
RTCPeerConnection . prototype . _createTransceiver = function ( kind , doNotAdd ) {
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 ,
associatedRemoteMediaStreams : [ ] ,
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 ;
}
if ( ! doNotAdd ) {
this . transceivers . push ( transceiver ) ;
}
return transceiver ;
} ;
RTCPeerConnection . prototype . addTrack = function ( track , stream ) {
if ( this . _isClosed ) {
throw makeError ( 'InvalidStateError' ,
'Attempted to call addTrack on a closed peerconnection.' ) ;
}
var alreadyExists = this . transceivers . find ( function ( s ) {
return s . track === track ;
} ) ;
if ( alreadyExists ) {
throw makeError ( 'InvalidAccessError' , 'Track already exists.' ) ;
}
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 ) ;
}
this . _maybeFireNegotiationNeeded ( ) ;
if ( this . localStreams . indexOf ( stream ) === - 1 ) {
this . localStreams . push ( stream ) ;
}
transceiver . track = track ;
transceiver . stream = stream ;
transceiver . rtpSender = new window . RTCRtpSender ( track ,
transceiver . dtlsTransport ) ;
return transceiver . rtpSender ;
} ;
RTCPeerConnection . prototype . addStream = function ( stream ) {
var pc = this ;
if ( edgeVersion >= 15025 ) {
stream . getTracks ( ) . forEach ( function ( track ) {
pc . addTrack ( track , stream ) ;
} ) ;
} else {
// Clone is necessary for local demos mostly, attaching directly
// to two different senders does not work (build 10547).
// Fixed in 15025 (or earlier)
var clonedStream = stream . clone ( ) ;
stream . getTracks ( ) . forEach ( function ( track , idx ) {
var clonedTrack = clonedStream . getTracks ( ) [ idx ] ;
track . addEventListener ( 'enabled' , function ( event ) {
clonedTrack . enabled = event . enabled ;
} ) ;
} ) ;
clonedStream . getTracks ( ) . forEach ( function ( track ) {
pc . addTrack ( track , clonedStream ) ;
} ) ;
}
} ;
RTCPeerConnection . prototype . removeTrack = function ( sender ) {
if ( this . _isClosed ) {
throw makeError ( 'InvalidStateError' ,
'Attempted to call removeTrack on a closed peerconnection.' ) ;
}
if ( ! ( sender instanceof window . RTCRtpSender ) ) {
throw new TypeError ( 'Argument 1 of RTCPeerConnection.removeTrack ' +
'does not implement interface RTCRtpSender.' ) ;
}
var transceiver = this . transceivers . find ( function ( t ) {
return t . rtpSender === sender ;
} ) ;
if ( ! transceiver ) {
throw makeError ( 'InvalidAccessError' ,
'Sender was not created by this connection.' ) ;
}
var stream = transceiver . stream ;
transceiver . rtpSender . stop ( ) ;
transceiver . rtpSender = null ;
transceiver . track = null ;
transceiver . stream = null ;
// remove the stream from the set of local streams
var localStreams = this . transceivers . map ( function ( t ) {
return t . stream ;
} ) ;
if ( localStreams . indexOf ( stream ) === - 1 &&
this . localStreams . indexOf ( stream ) > - 1 ) {
this . localStreams . splice ( this . localStreams . indexOf ( stream ) , 1 ) ;
}
this . _maybeFireNegotiationNeeded ( ) ;
} ;
RTCPeerConnection . prototype . removeStream = function ( stream ) {
var pc = this ;
stream . getTracks ( ) . forEach ( function ( track ) {
var sender = pc . getSenders ( ) . find ( function ( s ) {
return s . track === track ;
} ) ;
if ( sender ) {
pc . removeTrack ( sender ) ;
}
} ) ;
} ;
RTCPeerConnection . prototype . getSenders = function ( ) {
return this . transceivers . filter ( function ( transceiver ) {
return ! ! transceiver . rtpSender ;
} )
. map ( function ( transceiver ) {
return transceiver . rtpSender ;
} ) ;
} ;
RTCPeerConnection . prototype . getReceivers = function ( ) {
return this . transceivers . filter ( function ( transceiver ) {
return ! ! transceiver . rtpReceiver ;
} )
. map ( function ( transceiver ) {
return transceiver . rtpReceiver ;
} ) ;
} ;
RTCPeerConnection . prototype . _createIceGatherer = function ( sdpMLineIndex ,
usingBundle ) {
var pc = this ;
if ( usingBundle && sdpMLineIndex > 0 ) {
return this . transceivers [ 0 ] . iceGatherer ;
} else if ( this . _iceGatherers . length ) {
return this . _iceGatherers . shift ( ) ;
}
var iceGatherer = new window . RTCIceGatherer ( {
iceServers : this . _config . iceServers ,
gatherPolicy : this . _config . iceTransportPolicy
} ) ;
Object . defineProperty ( iceGatherer , 'state' ,
{ value : 'new' , writable : true }
) ;
this . transceivers [ sdpMLineIndex ] . bufferedCandidateEvents = [ ] ;
this . transceivers [ sdpMLineIndex ] . bufferCandidates = function ( event ) {
var end = ! event . candidate || Object . keys ( event . candidate ) . length === 0 ;
// polyfill since RTCIceGatherer.state is not implemented in
// Edge 10547 yet.
iceGatherer . state = end ? 'completed' : 'gathering' ;
if ( pc . transceivers [ sdpMLineIndex ] . bufferedCandidateEvents !== null ) {
pc . transceivers [ sdpMLineIndex ] . bufferedCandidateEvents . push ( event ) ;
}
} ;
iceGatherer . addEventListener ( 'localcandidate' ,
this . transceivers [ sdpMLineIndex ] . bufferCandidates ) ;
return iceGatherer ;
} ;
// start gathering from an RTCIceGatherer.
RTCPeerConnection . prototype . _gather = function ( mid , sdpMLineIndex ) {
var pc = this ;
var iceGatherer = this . transceivers [ sdpMLineIndex ] . iceGatherer ;
if ( iceGatherer . onlocalcandidate ) {
return ;
}
var bufferedCandidateEvents =
this . transceivers [ sdpMLineIndex ] . bufferedCandidateEvents ;
this . transceivers [ sdpMLineIndex ] . bufferedCandidateEvents = null ;
iceGatherer . removeEventListener ( 'localcandidate' ,
this . transceivers [ sdpMLineIndex ] . bufferCandidates ) ;
iceGatherer . onlocalcandidate = function ( evt ) {
if ( pc . usingBundle && sdpMLineIndex > 0 ) {
// if we know that we use bundle we can drop candidates with
// ѕ dpMLineIndex > 0. If we don't do this then our state gets
// confused since we dispose the extra ice gatherer.
return ;
}
var event = new Event ( 'icecandidate' ) ;
event . candidate = { sdpMid : mid , sdpMLineIndex : sdpMLineIndex } ;
var cand = evt . candidate ;
// Edge emits an empty object for RTCIceCandidateComplete‥
var end = ! cand || Object . keys ( cand ) . length === 0 ;
if ( end ) {
// polyfill since RTCIceGatherer.state is not implemented in
// Edge 10547 yet.
if ( iceGatherer . state === 'new' || iceGatherer . state === 'gathering' ) {
iceGatherer . state = 'completed' ;
}
} else {
if ( iceGatherer . state === 'new' ) {
iceGatherer . state = 'gathering' ;
}
// RTCIceCandidate doesn't have a component, needs to be added
cand . component = 1 ;
// also the usernameFragment. TODO: update SDP to take both variants.
cand . ufrag = iceGatherer . getLocalParameters ( ) . usernameFragment ;
var serializedCandidate = SDPUtils . writeCandidate ( cand ) ;
event . candidate = Object . assign ( event . candidate ,
SDPUtils . parseCandidate ( serializedCandidate ) ) ;
event . candidate . candidate = serializedCandidate ;
event . candidate . toJSON = function ( ) {
return {
candidate : event . candidate . candidate ,
sdpMid : event . candidate . sdpMid ,
sdpMLineIndex : event . candidate . sdpMLineIndex ,
usernameFragment : event . candidate . usernameFragment
} ;
} ;
}
// update local description.
var sections = SDPUtils . getMediaSections ( pc . _localDescription . sdp ) ;
if ( ! end ) {
sections [ event . candidate . sdpMLineIndex ] +=
'a=' + event . candidate . candidate + '\r\n' ;
} else {
sections [ event . candidate . sdpMLineIndex ] +=
'a=end-of-candidates\r\n' ;
}
pc . _localDescription . sdp =
SDPUtils . getDescription ( pc . _localDescription . sdp ) +
sections . join ( '' ) ;
var complete = pc . transceivers . every ( function ( transceiver ) {
return transceiver . iceGatherer &&
transceiver . iceGatherer . state === 'completed' ;
} ) ;
if ( pc . iceGatheringState !== 'gathering' ) {
pc . iceGatheringState = 'gathering' ;
pc . _emitGatheringStateChange ( ) ;
}
// Emit candidate. Also emit null candidate when all gatherers are
// complete.
if ( ! end ) {
pc . _dispatchEvent ( 'icecandidate' , event ) ;
}
if ( complete ) {
pc . _dispatchEvent ( 'icecandidate' , new Event ( 'icecandidate' ) ) ;
pc . iceGatheringState = 'complete' ;
pc . _emitGatheringStateChange ( ) ;
}
} ;
// emit already gathered candidates.
window . setTimeout ( function ( ) {
bufferedCandidateEvents . forEach ( function ( e ) {
iceGatherer . onlocalcandidate ( e ) ;
} ) ;
} , 0 ) ;
} ;
// Create ICE transport and DTLS transport.
RTCPeerConnection . prototype . _createIceAndDtlsTransports = function ( ) {
var pc = this ;
var iceTransport = new window . RTCIceTransport ( null ) ;
iceTransport . onicestatechange = function ( ) {
pc . _updateIceConnectionState ( ) ;
pc . _updateConnectionState ( ) ;
} ;
var dtlsTransport = new window . RTCDtlsTransport ( iceTransport ) ;
dtlsTransport . ondtlsstatechange = function ( ) {
pc . _updateConnectionState ( ) ;
} ;
dtlsTransport . onerror = function ( ) {
// onerror does not set state to failed by itself.
Object . defineProperty ( dtlsTransport , 'state' ,
{ value : 'failed' , writable : true } ) ;
pc . _updateConnectionState ( ) ;
} ;
return {
iceTransport : iceTransport ,
dtlsTransport : dtlsTransport
} ;
} ;
// Destroy ICE gatherer, ICE transport and DTLS transport.
// Without triggering the callbacks.
RTCPeerConnection . prototype . _disposeIceAndDtlsTransports = function (
sdpMLineIndex ) {
var iceGatherer = this . transceivers [ sdpMLineIndex ] . iceGatherer ;
if ( iceGatherer ) {
delete iceGatherer . onlocalcandidate ;
delete this . transceivers [ sdpMLineIndex ] . iceGatherer ;
}
var iceTransport = this . transceivers [ sdpMLineIndex ] . iceTransport ;
if ( iceTransport ) {
delete iceTransport . onicestatechange ;
delete this . transceivers [ sdpMLineIndex ] . iceTransport ;
}
var dtlsTransport = this . transceivers [ sdpMLineIndex ] . dtlsTransport ;
if ( dtlsTransport ) {
delete dtlsTransport . ondtlsstatechange ;
delete dtlsTransport . onerror ;
delete this . transceivers [ sdpMLineIndex ] . dtlsTransport ;
}
} ;
// Start the RTP Sender and Receiver for a transceiver.
RTCPeerConnection . prototype . _transceive = function ( transceiver ,
send , recv ) {
var params = getCommonCapabilities ( transceiver . localCapabilities ,
transceiver . remoteCapabilities ) ;
if ( send && transceiver . rtpSender ) {
params . encodings = transceiver . sendEncodingParameters ;
params . rtcp = {
cname : SDPUtils . localCName ,
compound : transceiver . rtcpParameters . compound
} ;
if ( transceiver . recvEncodingParameters . length ) {
params . rtcp . ssrc = transceiver . recvEncodingParameters [ 0 ] . ssrc ;
}
transceiver . rtpSender . send ( params ) ;
}
if ( recv && transceiver . rtpReceiver && params . codecs . length > 0 ) {
// remove RTX field in Edge 14942
if ( transceiver . kind === 'video'
&& transceiver . recvEncodingParameters
&& edgeVersion < 15019 ) {
transceiver . recvEncodingParameters . forEach ( function ( p ) {
delete p . rtx ;
} ) ;
}
if ( transceiver . recvEncodingParameters . length ) {
params . encodings = transceiver . recvEncodingParameters ;
} else {
params . encodings = [ { } ] ;
}
params . rtcp = {
compound : transceiver . rtcpParameters . compound
} ;
if ( transceiver . rtcpParameters . cname ) {
params . rtcp . cname = transceiver . rtcpParameters . cname ;
}
if ( transceiver . sendEncodingParameters . length ) {
params . rtcp . ssrc = transceiver . sendEncodingParameters [ 0 ] . ssrc ;
}
transceiver . rtpReceiver . receive ( params ) ;
}
} ;
RTCPeerConnection . prototype . setLocalDescription = function ( description ) {
var pc = this ;
// Note: pranswer is not supported.
if ( [ 'offer' , 'answer' ] . indexOf ( description . type ) === - 1 ) {
return Promise . reject ( makeError ( 'TypeError' ,
'Unsupported type "' + description . type + '"' ) ) ;
}
if ( ! isActionAllowedInSignalingState ( 'setLocalDescription' ,
description . type , pc . signalingState ) || pc . _isClosed ) {
return Promise . reject ( makeError ( 'InvalidStateError' ,
'Can not set local ' + description . type +
' in state ' + pc . signalingState ) ) ;
}
var sections ;
var sessionpart ;
if ( description . type === 'offer' ) {
// 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 ) ;
pc . transceivers [ sdpMLineIndex ] . localCapabilities = caps ;
} ) ;
pc . transceivers . forEach ( function ( transceiver , sdpMLineIndex ) {
pc . _gather ( transceiver . mid , sdpMLineIndex ) ;
} ) ;
} else if ( description . type === 'answer' ) {
sections = SDPUtils . splitSections ( pc . _remoteDescription . sdp ) ;
sessionpart = sections . shift ( ) ;
var isIceLite = SDPUtils . matchPrefix ( sessionpart ,
'a=ice-lite' ) . length > 0 ;
sections . forEach ( function ( mediaSection , sdpMLineIndex ) {
var transceiver = pc . transceivers [ sdpMLineIndex ] ;
var iceGatherer = transceiver . iceGatherer ;
var iceTransport = transceiver . iceTransport ;
var dtlsTransport = transceiver . dtlsTransport ;
var localCapabilities = transceiver . localCapabilities ;
var remoteCapabilities = transceiver . remoteCapabilities ;
// treat bundle-only as not-rejected.
var rejected = SDPUtils . isRejected ( mediaSection ) &&
SDPUtils . matchPrefix ( mediaSection , 'a=bundle-only' ) . length === 0 ;
if ( ! rejected && ! transceiver . rejected ) {
var remoteIceParameters = SDPUtils . getIceParameters (
mediaSection , sessionpart ) ;
var remoteDtlsParameters = SDPUtils . getDtlsParameters (
mediaSection , sessionpart ) ;
if ( isIceLite ) {
remoteDtlsParameters . role = 'server' ;
}
if ( ! pc . usingBundle || sdpMLineIndex === 0 ) {
pc . _gather ( transceiver . mid , sdpMLineIndex ) ;
if ( iceTransport . state === 'new' ) {
iceTransport . start ( iceGatherer , remoteIceParameters ,
isIceLite ? 'controlling' : 'controlled' ) ;
}
if ( dtlsTransport . state === 'new' ) {
dtlsTransport . start ( remoteDtlsParameters ) ;
}
}
// Calculate intersection of capabilities.
var params = getCommonCapabilities ( localCapabilities ,
remoteCapabilities ) ;
// Start the RTCRtpSender. The RTCRtpReceiver for this
// transceiver has already been started in setRemoteDescription.
pc . _transceive ( transceiver ,
params . codecs . length > 0 ,
false ) ;
}
} ) ;
}
pc . _localDescription = {
type : description . type ,
sdp : description . sdp
} ;
if ( description . type === 'offer' ) {
pc . _updateSignalingState ( 'have-local-offer' ) ;
} else {
pc . _updateSignalingState ( 'stable' ) ;
}
return Promise . resolve ( ) ;
} ;
RTCPeerConnection . prototype . setRemoteDescription = function ( description ) {
var pc = this ;
// Note: pranswer is not supported.
if ( [ 'offer' , 'answer' ] . indexOf ( description . type ) === - 1 ) {
return Promise . reject ( makeError ( 'TypeError' ,
'Unsupported type "' + description . type + '"' ) ) ;
}
if ( ! isActionAllowedInSignalingState ( 'setRemoteDescription' ,
description . type , pc . signalingState ) || pc . _isClosed ) {
return Promise . reject ( makeError ( 'InvalidStateError' ,
'Can not set remote ' + description . type +
' in state ' + pc . signalingState ) ) ;
}
var streams = { } ;
pc . remoteStreams . forEach ( function ( stream ) {
streams [ stream . id ] = stream ;
} ) ;
var receiverList = [ ] ;
var sections = SDPUtils . splitSections ( description . sdp ) ;
var sessionpart = sections . shift ( ) ;
var isIceLite = SDPUtils . matchPrefix ( sessionpart ,
'a=ice-lite' ) . length > 0 ;
var usingBundle = SDPUtils . matchPrefix ( sessionpart ,
'a=group:BUNDLE ' ) . length > 0 ;
pc . usingBundle = usingBundle ;
var iceOptions = SDPUtils . matchPrefix ( sessionpart ,
'a=ice-options:' ) [ 0 ] ;
if ( iceOptions ) {
pc . canTrickleIceCandidates = iceOptions . substr ( 14 ) . split ( ' ' )
. indexOf ( 'trickle' ) >= 0 ;
} else {
pc . canTrickleIceCandidates = false ;
}
sections . forEach ( function ( mediaSection , sdpMLineIndex ) {
var lines = SDPUtils . splitLines ( mediaSection ) ;
var kind = SDPUtils . getKind ( mediaSection ) ;
// treat bundle-only as not-rejected.
var rejected = SDPUtils . isRejected ( mediaSection ) &&
SDPUtils . matchPrefix ( mediaSection , 'a=bundle-only' ) . length === 0 ;
var protocol = lines [ 0 ] . substr ( 2 ) . split ( ' ' ) [ 2 ] ;
var direction = SDPUtils . getDirection ( mediaSection , sessionpart ) ;
var remoteMsid = SDPUtils . parseMsid ( mediaSection ) ;
var mid = SDPUtils . getMid ( mediaSection ) || SDPUtils . generateIdentifier ( ) ;
// Reject datachannels which are not implemented yet.
if ( rejected || ( kind === 'application' && ( protocol === 'DTLS/SCTP' ||
protocol === 'UDP/DTLS/SCTP' ) ) ) {
// TODO: this is dangerous in the case where a non-rejected m-line
// becomes rejected.
pc . transceivers [ sdpMLineIndex ] = {
mid : mid ,
kind : kind ,
protocol : protocol ,
rejected : true
} ;
return ;
}
if ( ! rejected && pc . transceivers [ sdpMLineIndex ] &&
pc . transceivers [ sdpMLineIndex ] . rejected ) {
// recycle a rejected transceiver.
pc . transceivers [ sdpMLineIndex ] = pc . _createTransceiver ( kind , true ) ;
}
var transceiver ;
var iceGatherer ;
var iceTransport ;
var dtlsTransport ;
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 ) ;
remoteDtlsParameters . role = 'client' ;
}
recvEncodingParameters =
SDPUtils . parseRtpEncodingParameters ( mediaSection ) ;
var rtcpParameters = SDPUtils . parseRtcpParameters ( mediaSection ) ;
var isComplete = SDPUtils . matchPrefix ( mediaSection ,
'a=end-of-candidates' , sessionpart ) . length > 0 ;
var cands = SDPUtils . matchPrefix ( mediaSection , 'a=candidate:' )
. map ( function ( cand ) {
return SDPUtils . parseCandidate ( cand ) ;
} )
. filter ( function ( cand ) {
return cand . component === 1 ;
} ) ;
// Check if we can use BUNDLE and dispose transports.
if ( ( description . type === 'offer' || description . type === 'answer' ) &&
! rejected && usingBundle && sdpMLineIndex > 0 &&
pc . transceivers [ sdpMLineIndex ] ) {
pc . _disposeIceAndDtlsTransports ( sdpMLineIndex ) ;
pc . transceivers [ sdpMLineIndex ] . iceGatherer =
pc . transceivers [ 0 ] . iceGatherer ;
pc . transceivers [ sdpMLineIndex ] . iceTransport =
pc . transceivers [ 0 ] . iceTransport ;
pc . transceivers [ sdpMLineIndex ] . dtlsTransport =
pc . transceivers [ 0 ] . dtlsTransport ;
if ( pc . transceivers [ sdpMLineIndex ] . rtpSender ) {
pc . transceivers [ sdpMLineIndex ] . rtpSender . setTransport (
pc . transceivers [ 0 ] . dtlsTransport ) ;
}
if ( pc . transceivers [ sdpMLineIndex ] . rtpReceiver ) {
pc . transceivers [ sdpMLineIndex ] . rtpReceiver . setTransport (
pc . transceivers [ 0 ] . dtlsTransport ) ;
}
}
if ( description . type === 'offer' && ! rejected ) {
transceiver = pc . transceivers [ sdpMLineIndex ] ||
pc . _createTransceiver ( kind ) ;
transceiver . mid = mid ;
if ( ! transceiver . iceGatherer ) {
transceiver . iceGatherer = pc . _createIceGatherer ( sdpMLineIndex ,
usingBundle ) ;
}
if ( cands . length && transceiver . iceTransport . state === 'new' ) {
if ( isComplete && ( ! usingBundle || sdpMLineIndex === 0 ) ) {
transceiver . iceTransport . setRemoteCandidates ( cands ) ;
} else {
cands . forEach ( function ( candidate ) {
maybeAddCandidate ( transceiver . iceTransport , candidate ) ;
} ) ;
}
}
localCapabilities = window . RTCRtpReceiver . getCapabilities ( kind ) ;
// filter RTX until additional stuff needed for RTX is implemented
// in adapter.js
if ( edgeVersion < 15019 ) {
localCapabilities . codecs = localCapabilities . codecs . filter (
function ( codec ) {
return codec . name !== 'rtx' ;
} ) ;
}
sendEncodingParameters = transceiver . sendEncodingParameters || [ {
ssrc : ( 2 * sdpMLineIndex + 2 ) * 1001
} ] ;
// TODO: rewrite to use http://w3c.github.io/webrtc-pc/#set-associated-remote-streams
var isNewTrack = false ;
if ( direction === 'sendrecv' || direction === 'sendonly' ) {
isNewTrack = ! transceiver . rtpReceiver ;
rtpReceiver = transceiver . rtpReceiver ||
new window . RTCRtpReceiver ( transceiver . dtlsTransport , kind ) ;
if ( isNewTrack ) {
var stream ;
track = rtpReceiver . track ;
// FIXME: does not work with Plan B.
if ( remoteMsid && remoteMsid . stream === '-' ) {
// no-op. a stream id of '-' means: no associated stream.
} else if ( remoteMsid ) {
if ( ! streams [ remoteMsid . stream ] ) {
streams [ remoteMsid . stream ] = new window . MediaStream ( ) ;
Object . defineProperty ( streams [ remoteMsid . stream ] , 'id' , {
get : function ( ) {
return remoteMsid . stream ;
}
} ) ;
}
Object . defineProperty ( track , 'id' , {
get : function ( ) {
return remoteMsid . track ;
}
} ) ;
stream = streams [ remoteMsid . stream ] ;
} else {
if ( ! streams . default ) {
streams . default = new window . MediaStream ( ) ;
}
stream = streams . default ;
}
if ( stream ) {
addTrackToStreamAndFireEvent ( track , stream ) ;
transceiver . associatedRemoteMediaStreams . push ( stream ) ;
}
receiverList . push ( [ track , rtpReceiver , stream ] ) ;
}
} else if ( transceiver . rtpReceiver && transceiver . rtpReceiver . track ) {
transceiver . associatedRemoteMediaStreams . forEach ( function ( s ) {
var nativeTrack = s . getTracks ( ) . find ( function ( t ) {
return t . id === transceiver . rtpReceiver . track . id ;
} ) ;
if ( nativeTrack ) {
removeTrackFromStreamAndFireEvent ( nativeTrack , s ) ;
}
} ) ;
transceiver . associatedRemoteMediaStreams = [ ] ;
}
transceiver . localCapabilities = localCapabilities ;
transceiver . remoteCapabilities = remoteCapabilities ;
transceiver . rtpReceiver = rtpReceiver ;
transceiver . rtcpParameters = rtcpParameters ;
transceiver . sendEncodingParameters = sendEncodingParameters ;
transceiver . recvEncodingParameters = recvEncodingParameters ;
// Start the RTCRtpReceiver now. The RTPSender is started in
// setLocalDescription.
pc . _transceive ( pc . transceivers [ sdpMLineIndex ] ,
false ,
isNewTrack ) ;
} else if ( description . type === 'answer' && ! rejected ) {
transceiver = pc . transceivers [ sdpMLineIndex ] ;
iceGatherer = transceiver . iceGatherer ;
iceTransport = transceiver . iceTransport ;
dtlsTransport = transceiver . dtlsTransport ;
rtpReceiver = transceiver . rtpReceiver ;
sendEncodingParameters = transceiver . sendEncodingParameters ;
localCapabilities = transceiver . localCapabilities ;
pc . transceivers [ sdpMLineIndex ] . recvEncodingParameters =
recvEncodingParameters ;
pc . transceivers [ sdpMLineIndex ] . remoteCapabilities =
remoteCapabilities ;
pc . transceivers [ sdpMLineIndex ] . rtcpParameters = rtcpParameters ;
if ( cands . length && iceTransport . state === 'new' ) {
if ( ( isIceLite || isComplete ) &&
( ! usingBundle || sdpMLineIndex === 0 ) ) {
iceTransport . setRemoteCandidates ( cands ) ;
} else {
cands . forEach ( function ( candidate ) {
maybeAddCandidate ( transceiver . iceTransport , candidate ) ;
} ) ;
}
}
if ( ! usingBundle || sdpMLineIndex === 0 ) {
if ( iceTransport . state === 'new' ) {
iceTransport . start ( iceGatherer , remoteIceParameters ,
'controlling' ) ;
}
if ( dtlsTransport . state === 'new' ) {
dtlsTransport . start ( remoteDtlsParameters ) ;
}
}
// If the offer contained RTX but the answer did not,
// remove RTX from sendEncodingParameters.
var commonCapabilities = getCommonCapabilities (
transceiver . localCapabilities ,
transceiver . remoteCapabilities ) ;
var hasRtx = commonCapabilities . codecs . filter ( function ( c ) {
return c . name . toLowerCase ( ) === 'rtx' ;
} ) . length ;
if ( ! hasRtx && transceiver . sendEncodingParameters [ 0 ] . rtx ) {
delete transceiver . sendEncodingParameters [ 0 ] . rtx ;
}
pc . _transceive ( transceiver ,
direction === 'sendrecv' || direction === 'recvonly' ,
direction === 'sendrecv' || direction === 'sendonly' ) ;
// TODO: rewrite to use http://w3c.github.io/webrtc-pc/#set-associated-remote-streams
if ( rtpReceiver &&
( direction === 'sendrecv' || direction === 'sendonly' ) ) {
track = rtpReceiver . track ;
if ( remoteMsid ) {
if ( ! streams [ remoteMsid . stream ] ) {
streams [ remoteMsid . stream ] = new window . MediaStream ( ) ;
}
addTrackToStreamAndFireEvent ( track , streams [ remoteMsid . stream ] ) ;
receiverList . push ( [ track , rtpReceiver , streams [ remoteMsid . stream ] ] ) ;
} else {
if ( ! streams . default ) {
streams . default = new window . MediaStream ( ) ;
}
addTrackToStreamAndFireEvent ( track , streams . default ) ;
receiverList . push ( [ track , rtpReceiver , streams . default ] ) ;
}
} else {
// FIXME: actually the receiver should be created later.
delete transceiver . rtpReceiver ;
}
}
} ) ;
if ( pc . _dtlsRole === undefined ) {
pc . _dtlsRole = description . type === 'offer' ? 'active' : 'passive' ;
}
pc . _remoteDescription = {
type : description . type ,
sdp : description . sdp
} ;
if ( description . type === 'offer' ) {
pc . _updateSignalingState ( 'have-remote-offer' ) ;
} else {
pc . _updateSignalingState ( 'stable' ) ;
}
Object . keys ( streams ) . forEach ( function ( sid ) {
var stream = streams [ sid ] ;
if ( stream . getTracks ( ) . length ) {
if ( pc . remoteStreams . indexOf ( stream ) === - 1 ) {
pc . remoteStreams . push ( stream ) ;
var event = new Event ( 'addstream' ) ;
event . stream = stream ;
window . setTimeout ( function ( ) {
pc . _dispatchEvent ( 'addstream' , event ) ;
} ) ;
}
receiverList . forEach ( function ( item ) {
var track = item [ 0 ] ;
var receiver = item [ 1 ] ;
if ( stream . id !== item [ 2 ] . id ) {
return ;
}
fireAddTrack ( pc , track , receiver , [ stream ] ) ;
} ) ;
}
} ) ;
receiverList . forEach ( function ( item ) {
if ( item [ 2 ] ) {
return ;
}
fireAddTrack ( pc , item [ 0 ] , item [ 1 ] , [ ] ) ;
} ) ;
// check whether addIceCandidate({}) was called within four seconds after
// setRemoteDescription.
window . setTimeout ( function ( ) {
if ( ! ( pc && pc . transceivers ) ) {
return ;
}
pc . 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 ) ;
return Promise . resolve ( ) ;
} ;
RTCPeerConnection . prototype . close = function ( ) {
this . transceivers . forEach ( function ( transceiver ) {
/ * n o t y e t
if ( transceiver . iceGatherer ) {
transceiver . iceGatherer . close ( ) ;
}
* /
if ( transceiver . iceTransport ) {
transceiver . iceTransport . stop ( ) ;
}
if ( transceiver . dtlsTransport ) {
transceiver . dtlsTransport . stop ( ) ;
}
if ( transceiver . rtpSender ) {
transceiver . rtpSender . stop ( ) ;
}
if ( transceiver . rtpReceiver ) {
transceiver . rtpReceiver . stop ( ) ;
}
} ) ;
// FIXME: clean up tracks, local streams, remote streams, etc
this . _isClosed = true ;
this . _updateSignalingState ( 'closed' ) ;
} ;
// Update the signaling state.
RTCPeerConnection . prototype . _updateSignalingState = function ( newState ) {
this . signalingState = newState ;
var event = new Event ( 'signalingstatechange' ) ;
this . _dispatchEvent ( 'signalingstatechange' , event ) ;
} ;
// Determine whether to fire the negotiationneeded event.
RTCPeerConnection . prototype . _maybeFireNegotiationNeeded = function ( ) {
var pc = this ;
if ( this . signalingState !== 'stable' || this . needNegotiation === true ) {
return ;
}
this . needNegotiation = true ;
window . setTimeout ( function ( ) {
if ( pc . needNegotiation ) {
pc . needNegotiation = false ;
var event = new Event ( 'negotiationneeded' ) ;
pc . _dispatchEvent ( 'negotiationneeded' , event ) ;
}
} , 0 ) ;
} ;
// Update the ice connection state.
RTCPeerConnection . prototype . _updateIceConnectionState = function ( ) {
var newState ;
var states = {
'new' : 0 ,
closed : 0 ,
checking : 0 ,
connected : 0 ,
completed : 0 ,
disconnected : 0 ,
failed : 0
} ;
this . transceivers . forEach ( function ( transceiver ) {
if ( transceiver . iceTransport && ! transceiver . rejected ) {
states [ transceiver . iceTransport . state ] ++ ;
}
} ) ;
newState = 'new' ;
if ( states . failed > 0 ) {
newState = 'failed' ;
} else if ( states . checking > 0 ) {
newState = 'checking' ;
} else if ( states . disconnected > 0 ) {
newState = 'disconnected' ;
} else if ( states . new > 0 ) {
newState = 'new' ;
} else if ( states . connected > 0 ) {
newState = 'connected' ;
} else if ( states . completed > 0 ) {
newState = 'completed' ;
}
if ( newState !== this . iceConnectionState ) {
this . iceConnectionState = newState ;
var event = new Event ( 'iceconnectionstatechange' ) ;
this . _dispatchEvent ( 'iceconnectionstatechange' , event ) ;
}
} ;
// Update the connection state.
RTCPeerConnection . prototype . _updateConnectionState = function ( ) {
var newState ;
var states = {
'new' : 0 ,
closed : 0 ,
connecting : 0 ,
connected : 0 ,
completed : 0 ,
disconnected : 0 ,
failed : 0
} ;
this . transceivers . forEach ( function ( transceiver ) {
if ( transceiver . iceTransport && transceiver . dtlsTransport &&
! transceiver . rejected ) {
states [ transceiver . iceTransport . state ] ++ ;
states [ transceiver . dtlsTransport . state ] ++ ;
}
} ) ;
// ICETransport.completed and connected are the same for this purpose.
states . connected += states . completed ;
newState = 'new' ;
if ( states . failed > 0 ) {
newState = 'failed' ;
} else if ( states . connecting > 0 ) {
newState = 'connecting' ;
} else if ( states . disconnected > 0 ) {
newState = 'disconnected' ;
} else if ( states . new > 0 ) {
newState = 'new' ;
} else if ( states . connected > 0 ) {
newState = 'connected' ;
}
if ( newState !== this . connectionState ) {
this . connectionState = newState ;
var event = new Event ( 'connectionstatechange' ) ;
this . _dispatchEvent ( 'connectionstatechange' , event ) ;
}
} ;
RTCPeerConnection . prototype . createOffer = function ( ) {
var pc = this ;
if ( pc . _isClosed ) {
return Promise . reject ( makeError ( 'InvalidStateError' ,
'Can not call createOffer after close' ) ) ;
}
var numAudioTracks = pc . transceivers . filter ( function ( t ) {
return t . kind === 'audio' ;
} ) . length ;
var numVideoTracks = pc . transceivers . filter ( function ( t ) {
return t . kind === 'video' ;
} ) . length ;
// Determine number of audio and video tracks we need to send/recv.
var offerOptions = arguments [ 0 ] ;
if ( offerOptions ) {
// Reject Chrome legacy constraints.
if ( offerOptions . mandatory || offerOptions . optional ) {
throw new TypeError (
'Legacy mandatory/optional constraints not supported.' ) ;
}
if ( offerOptions . offerToReceiveAudio !== undefined ) {
if ( offerOptions . offerToReceiveAudio === true ) {
numAudioTracks = 1 ;
} else if ( offerOptions . offerToReceiveAudio === false ) {
numAudioTracks = 0 ;
} else {
numAudioTracks = offerOptions . offerToReceiveAudio ;
}
}
if ( offerOptions . offerToReceiveVideo !== undefined ) {
if ( offerOptions . offerToReceiveVideo === true ) {
numVideoTracks = 1 ;
} else if ( offerOptions . offerToReceiveVideo === false ) {
numVideoTracks = 0 ;
} else {
numVideoTracks = offerOptions . offerToReceiveVideo ;
}
}
}
pc . 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 ) {
pc . _createTransceiver ( 'audio' ) ;
numAudioTracks -- ;
}
if ( numVideoTracks > 0 ) {
pc . _createTransceiver ( 'video' ) ;
numVideoTracks -- ;
}
}
var sdp = SDPUtils . writeSessionBoilerplate ( pc . _sdpSessionId ,
pc . _sdpSessionVersion ++ ) ;
pc . transceivers . forEach ( function ( transceiver , sdpMLineIndex ) {
// For each track, create an ice gatherer, ice transport,
// dtls transport, potentially rtpsender and rtpreceiver.
var track = transceiver . track ;
var kind = transceiver . kind ;
var mid = transceiver . mid || SDPUtils . generateIdentifier ( ) ;
transceiver . mid = mid ;
if ( ! transceiver . iceGatherer ) {
transceiver . iceGatherer = pc . _createIceGatherer ( sdpMLineIndex ,
pc . usingBundle ) ;
}
var localCapabilities = window . RTCRtpSender . getCapabilities ( kind ) ;
// filter RTX until additional stuff needed for RTX is implemented
// in adapter.js
if ( edgeVersion < 15019 ) {
localCapabilities . codecs = localCapabilities . codecs . filter (
function ( codec ) {
return codec . name !== 'rtx' ;
} ) ;
}
localCapabilities . codecs . forEach ( function ( codec ) {
// work around https://bugs.chromium.org/p/webrtc/issues/detail?id=6552
// by adding level-asymmetry-allowed=1
if ( codec . name === 'H264' &&
codec . parameters [ 'level-asymmetry-allowed' ] === undefined ) {
codec . parameters [ 'level-asymmetry-allowed' ] = '1' ;
}
// for subsequent offers, we might have to re-use the payload
// type of the last offer.
if ( transceiver . remoteCapabilities &&
transceiver . remoteCapabilities . codecs ) {
transceiver . remoteCapabilities . codecs . forEach ( function ( remoteCodec ) {
if ( codec . name . toLowerCase ( ) === remoteCodec . name . toLowerCase ( ) &&
codec . clockRate === remoteCodec . clockRate ) {
codec . preferredPayloadType = remoteCodec . payloadType ;
}
} ) ;
}
} ) ;
localCapabilities . headerExtensions . forEach ( function ( hdrExt ) {
var remoteExtensions = transceiver . remoteCapabilities &&
transceiver . remoteCapabilities . headerExtensions || [ ] ;
remoteExtensions . forEach ( function ( rHdrExt ) {
if ( hdrExt . uri === rHdrExt . uri ) {
hdrExt . id = rHdrExt . id ;
}
} ) ;
} ) ;
// generate an ssrc now, to be used later in rtpSender.send
var sendEncodingParameters = transceiver . sendEncodingParameters || [ {
ssrc : ( 2 * sdpMLineIndex + 1 ) * 1001
} ] ;
if ( track ) {
// add RTX
if ( edgeVersion >= 15019 && kind === 'video' &&
! sendEncodingParameters [ 0 ] . rtx ) {
sendEncodingParameters [ 0 ] . rtx = {
ssrc : sendEncodingParameters [ 0 ] . ssrc + 1
} ;
}
}
if ( transceiver . wantReceive ) {
transceiver . rtpReceiver = new window . RTCRtpReceiver (
transceiver . dtlsTransport , kind ) ;
}
transceiver . localCapabilities = localCapabilities ;
transceiver . sendEncodingParameters = sendEncodingParameters ;
} ) ;
// always offer BUNDLE and dispose on return if not supported.
if ( pc . _config . bundlePolicy !== 'max-compat' ) {
sdp += 'a=group:BUNDLE ' + pc . transceivers . map ( function ( t ) {
return t . mid ;
} ) . join ( ' ' ) + '\r\n' ;
}
sdp += 'a=ice-options:trickle\r\n' ;
pc . transceivers . forEach ( function ( transceiver , sdpMLineIndex ) {
sdp += writeMediaSection ( transceiver , transceiver . localCapabilities ,
'offer' , transceiver . stream , pc . _dtlsRole ) ;
sdp += 'a=rtcp-rsize\r\n' ;
if ( transceiver . iceGatherer && pc . iceGatheringState !== 'new' &&
( sdpMLineIndex === 0 || ! pc . usingBundle ) ) {
transceiver . iceGatherer . getLocalCandidates ( ) . forEach ( function ( cand ) {
cand . component = 1 ;
sdp += 'a=' + SDPUtils . writeCandidate ( cand ) + '\r\n' ;
} ) ;
if ( transceiver . iceGatherer . state === 'completed' ) {
sdp += 'a=end-of-candidates\r\n' ;
}
}
} ) ;
var desc = new window . RTCSessionDescription ( {
type : 'offer' ,
sdp : sdp
} ) ;
return Promise . resolve ( desc ) ;
} ;
RTCPeerConnection . prototype . createAnswer = function ( ) {
var pc = this ;
if ( pc . _isClosed ) {
return Promise . reject ( makeError ( 'InvalidStateError' ,
'Can not call createAnswer after close' ) ) ;
}
if ( ! ( pc . signalingState === 'have-remote-offer' ||
pc . signalingState === 'have-local-pranswer' ) ) {
return Promise . reject ( makeError ( 'InvalidStateError' ,
'Can not call createAnswer in signalingState ' + pc . signalingState ) ) ;
}
var sdp = SDPUtils . writeSessionBoilerplate ( pc . _sdpSessionId ,
pc . _sdpSessionVersion ++ ) ;
if ( pc . usingBundle ) {
sdp += 'a=group:BUNDLE ' + pc . transceivers . map ( function ( t ) {
return t . mid ;
} ) . join ( ' ' ) + '\r\n' ;
}
sdp += 'a=ice-options:trickle\r\n' ;
var mediaSectionsInOffer = SDPUtils . getMediaSections (
pc . _remoteDescription . sdp ) . length ;
pc . transceivers . forEach ( function ( transceiver , sdpMLineIndex ) {
if ( sdpMLineIndex + 1 > mediaSectionsInOffer ) {
return ;
}
if ( transceiver . rejected ) {
if ( transceiver . kind === 'application' ) {
if ( transceiver . protocol === 'DTLS/SCTP' ) { // legacy fmt
sdp += 'm=application 0 DTLS/SCTP 5000\r\n' ;
} else {
sdp += 'm=application 0 ' + transceiver . protocol +
' webrtc-datachannel\r\n' ;
}
} else if ( transceiver . kind === 'audio' ) {
sdp += 'm=audio 0 UDP/TLS/RTP/SAVPF 0\r\n' +
'a=rtpmap:0 PCMU/8000\r\n' ;
} else if ( transceiver . kind === 'video' ) {
sdp += 'm=video 0 UDP/TLS/RTP/SAVPF 120\r\n' +
'a=rtpmap:120 VP8/90000\r\n' ;
}
sdp += 'c=IN IP4 0.0.0.0\r\n' +
'a=inactive\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 ) {
transceiver . sendEncodingParameters [ 0 ] . rtx = {
ssrc : transceiver . sendEncodingParameters [ 0 ] . ssrc + 1
} ;
}
}
}
// Calculate intersection of capabilities.
var commonCapabilities = getCommonCapabilities (
transceiver . localCapabilities ,
transceiver . remoteCapabilities ) ;
var hasRtx = commonCapabilities . codecs . filter ( function ( c ) {
return c . name . toLowerCase ( ) === 'rtx' ;
} ) . length ;
if ( ! hasRtx && transceiver . sendEncodingParameters [ 0 ] . rtx ) {
delete transceiver . sendEncodingParameters [ 0 ] . rtx ;
}
sdp += writeMediaSection ( transceiver , commonCapabilities ,
'answer' , transceiver . stream , pc . _dtlsRole ) ;
if ( transceiver . rtcpParameters &&
transceiver . rtcpParameters . reducedSize ) {
sdp += 'a=rtcp-rsize\r\n' ;
}
} ) ;
var desc = new window . RTCSessionDescription ( {
type : 'answer' ,
sdp : sdp
} ) ;
return Promise . resolve ( desc ) ;
} ;
RTCPeerConnection . prototype . addIceCandidate = function ( candidate ) {
var pc = this ;
var sections ;
if ( candidate && ! ( candidate . sdpMLineIndex !== undefined ||
candidate . sdpMid ) ) {
return Promise . reject ( new TypeError ( 'sdpMLineIndex or sdpMid required' ) ) ;
}
// TODO: needs to go into ops queue.
return new Promise ( function ( resolve , reject ) {
if ( ! pc . _remoteDescription ) {
return reject ( makeError ( 'InvalidStateError' ,
'Can not add ICE candidate without a remote description' ) ) ;
} else if ( ! candidate || candidate . candidate === '' ) {
for ( var j = 0 ; j < pc . transceivers . length ; j ++ ) {
if ( pc . transceivers [ j ] . rejected ) {
continue ;
}
pc . transceivers [ j ] . iceTransport . addRemoteCandidate ( { } ) ;
sections = SDPUtils . getMediaSections ( pc . _remoteDescription . sdp ) ;
sections [ j ] += 'a=end-of-candidates\r\n' ;
pc . _remoteDescription . sdp =
SDPUtils . getDescription ( pc . _remoteDescription . sdp ) +
sections . join ( '' ) ;
if ( pc . usingBundle ) {
break ;
}
}
} else {
var sdpMLineIndex = candidate . sdpMLineIndex ;
if ( candidate . sdpMid ) {
for ( var i = 0 ; i < pc . transceivers . length ; i ++ ) {
if ( pc . transceivers [ i ] . mid === candidate . sdpMid ) {
sdpMLineIndex = i ;
break ;
}
}
}
var transceiver = pc . transceivers [ sdpMLineIndex ] ;
if ( transceiver ) {
if ( transceiver . rejected ) {
return resolve ( ) ;
}
var cand = Object . keys ( candidate . candidate ) . length > 0 ?
SDPUtils . parseCandidate ( candidate . candidate ) : { } ;
// Ignore Chrome's invalid candidates since Edge does not like them.
if ( cand . protocol === 'tcp' && ( cand . port === 0 || cand . port === 9 ) ) {
return resolve ( ) ;
}
// Ignore RTCP candidates, we assume RTCP-MUX.
if ( cand . component && cand . component !== 1 ) {
return resolve ( ) ;
}
// when using bundle, avoid adding candidates to the wrong
// ice transport. And avoid adding candidates added in the SDP.
if ( sdpMLineIndex === 0 || ( sdpMLineIndex > 0 &&
transceiver . iceTransport !== pc . transceivers [ 0 ] . iceTransport ) ) {
if ( ! maybeAddCandidate ( transceiver . iceTransport , cand ) ) {
return reject ( makeError ( 'OperationError' ,
'Can not add ICE candidate' ) ) ;
}
}
// update the remoteDescription.
var candidateString = candidate . candidate . trim ( ) ;
if ( candidateString . indexOf ( 'a=' ) === 0 ) {
candidateString = candidateString . substr ( 2 ) ;
}
sections = SDPUtils . getMediaSections ( pc . _remoteDescription . sdp ) ;
sections [ sdpMLineIndex ] += 'a=' +
( cand . type ? candidateString : 'end-of-candidates' )
+ '\r\n' ;
pc . _remoteDescription . sdp =
SDPUtils . getDescription ( pc . _remoteDescription . sdp ) +
sections . join ( '' ) ;
} else {
return reject ( makeError ( 'OperationError' ,
'Can not add ICE candidate' ) ) ;
}
}
resolve ( ) ;
} ) ;
} ;
RTCPeerConnection . prototype . getStats = function ( selector ) {
if ( selector && selector instanceof window . MediaStreamTrack ) {
var senderOrReceiver = null ;
this . transceivers . forEach ( function ( transceiver ) {
if ( transceiver . rtpSender &&
transceiver . rtpSender . track === selector ) {
senderOrReceiver = transceiver . rtpSender ;
} else if ( transceiver . rtpReceiver &&
transceiver . rtpReceiver . track === selector ) {
senderOrReceiver = transceiver . rtpReceiver ;
}
} ) ;
if ( ! senderOrReceiver ) {
throw makeError ( 'InvalidAccessError' , 'Invalid selector.' ) ;
}
return senderOrReceiver . getStats ( ) ;
}
var promises = [ ] ;
this . transceivers . forEach ( function ( transceiver ) {
[ 'rtpSender' , 'rtpReceiver' , 'iceGatherer' , 'iceTransport' ,
'dtlsTransport' ] . forEach ( function ( method ) {
if ( transceiver [ method ] ) {
promises . push ( transceiver [ method ] . getStats ( ) ) ;
}
} ) ;
} ) ;
return Promise . all ( promises ) . then ( function ( allStats ) {
var results = new Map ( ) ;
allStats . forEach ( function ( stats ) {
stats . forEach ( function ( stat ) {
results . set ( stat . id , stat ) ;
} ) ;
} ) ;
return results ;
} ) ;
} ;
// fix low-level stat names and return Map instead of object.
var ortcObjects = [ 'RTCRtpSender' , 'RTCRtpReceiver' , 'RTCIceGatherer' ,
'RTCIceTransport' , 'RTCDtlsTransport' ] ;
ortcObjects . forEach ( function ( ortcObjectName ) {
var obj = window [ ortcObjectName ] ;
if ( obj && obj . prototype && obj . prototype . getStats ) {
var nativeGetstats = obj . prototype . getStats ;
obj . prototype . getStats = function ( ) {
return nativeGetstats . apply ( this )
. then ( function ( nativeStats ) {
var mapStats = new Map ( ) ;
Object . keys ( nativeStats ) . forEach ( function ( id ) {
nativeStats [ id ] . type = fixStatsType ( nativeStats [ id ] ) ;
mapStats . set ( id , nativeStats [ id ] ) ;
} ) ;
return mapStats ;
} ) ;
} ;
}
} ) ;
// legacy callback shims. Should be moved to adapter.js some days.
var methods = [ 'createOffer' , 'createAnswer' ] ;
methods . forEach ( function ( method ) {
var nativeMethod = RTCPeerConnection . prototype [ method ] ;
RTCPeerConnection . prototype [ method ] = function ( ) {
var args = arguments ;
if ( typeof args [ 0 ] === 'function' ||
typeof args [ 1 ] === 'function' ) { // legacy
return nativeMethod . apply ( this , [ arguments [ 2 ] ] )
. then ( function ( description ) {
if ( typeof args [ 0 ] === 'function' ) {
args [ 0 ] . apply ( null , [ description ] ) ;
}
} , function ( error ) {
if ( typeof args [ 1 ] === 'function' ) {
args [ 1 ] . apply ( null , [ error ] ) ;
}
} ) ;
}
return nativeMethod . apply ( this , arguments ) ;
} ;
} ) ;
methods = [ 'setLocalDescription' , 'setRemoteDescription' , 'addIceCandidate' ] ;
methods . forEach ( function ( method ) {
var nativeMethod = RTCPeerConnection . prototype [ method ] ;
RTCPeerConnection . prototype [ method ] = function ( ) {
var args = arguments ;
if ( typeof args [ 1 ] === 'function' ||
typeof args [ 2 ] === 'function' ) { // legacy
return nativeMethod . apply ( this , arguments )
. then ( function ( ) {
if ( typeof args [ 1 ] === 'function' ) {
args [ 1 ] . apply ( null ) ;
}
} , function ( error ) {
if ( typeof args [ 2 ] === 'function' ) {
args [ 2 ] . apply ( null , [ error ] ) ;
}
} ) ;
}
return nativeMethod . apply ( this , arguments ) ;
} ;
} ) ;
// getStats is special. It doesn't have a spec legacy method yet we support
// getStats(something, cb) without error callbacks.
[ 'getStats' ] . forEach ( function ( method ) {
var nativeMethod = RTCPeerConnection . prototype [ method ] ;
RTCPeerConnection . prototype [ method ] = function ( ) {
var args = arguments ;
if ( typeof args [ 1 ] === 'function' ) {
return nativeMethod . apply ( this , arguments )
. then ( function ( ) {
if ( typeof args [ 1 ] === 'function' ) {
args [ 1 ] . apply ( null ) ;
}
} ) ;
}
return nativeMethod . apply ( this , arguments ) ;
} ;
} ) ;
return RTCPeerConnection ;
} ;
} , { "sdp" : 17 } ] , 17 : [ function ( require , module , exports ) {
/* eslint-env node */
'use strict' ;
// SDP helpers.
var SDPUtils = { } ;
// Generate an alphanumeric identifier for cname or mids.
// TODO: use UUIDs instead? https://gist.github.com/jed/982883
SDPUtils . generateIdentifier = function ( ) {
return Math . random ( ) . toString ( 36 ) . substr ( 2 , 10 ) ;
} ;
// The RTCP CNAME used by all peerconnections from the same JS.
SDPUtils . localCName = SDPUtils . generateIdentifier ( ) ;
// Splits SDP into lines, dealing with both CRLF and LF.
SDPUtils . splitLines = function ( blob ) {
return blob . trim ( ) . split ( '\n' ) . map ( function ( line ) {
return line . trim ( ) ;
} ) ;
} ;
// Splits SDP into sessionpart and mediasections. Ensures CRLF.
SDPUtils . splitSections = function ( blob ) {
var parts = blob . split ( '\nm=' ) ;
return parts . map ( function ( part , index ) {
return ( index > 0 ? 'm=' + part : part ) . trim ( ) + '\r\n' ;
} ) ;
} ;
// returns the session description.
SDPUtils . getDescription = function ( blob ) {
var sections = SDPUtils . splitSections ( blob ) ;
return sections && sections [ 0 ] ;
} ;
// returns the individual media sections.
SDPUtils . getMediaSections = function ( blob ) {
var sections = SDPUtils . splitSections ( blob ) ;
sections . shift ( ) ;
return sections ;
} ;
// Returns lines that start with a certain prefix.
SDPUtils . matchPrefix = function ( blob , prefix ) {
return SDPUtils . splitLines ( blob ) . filter ( function ( line ) {
return line . indexOf ( prefix ) === 0 ;
} ) ;
} ;
// 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"
SDPUtils . parseCandidate = function ( line ) {
var parts ;
// Parse both variants.
if ( line . indexOf ( 'a=candidate:' ) === 0 ) {
parts = line . substring ( 12 ) . split ( ' ' ) ;
} else {
parts = line . substring ( 10 ) . split ( ' ' ) ;
}
var candidate = {
foundation : parts [ 0 ] ,
component : parseInt ( parts [ 1 ] , 10 ) ,
protocol : parts [ 2 ] . toLowerCase ( ) ,
priority : parseInt ( parts [ 3 ] , 10 ) ,
ip : parts [ 4 ] ,
address : parts [ 4 ] , // address is an alias for ip.
port : parseInt ( parts [ 5 ] , 10 ) ,
// skip parts[6] == 'typ'
type : parts [ 7 ]
} ;
for ( var i = 8 ; i < parts . length ; i += 2 ) {
switch ( parts [ i ] ) {
case 'raddr' :
candidate . relatedAddress = parts [ i + 1 ] ;
break ;
case 'rport' :
candidate . relatedPort = parseInt ( parts [ i + 1 ] , 10 ) ;
break ;
case 'tcptype' :
candidate . tcpType = parts [ i + 1 ] ;
break ;
case 'ufrag' :
candidate . ufrag = parts [ i + 1 ] ; // for backward compability.
candidate . usernameFragment = parts [ i + 1 ] ;
break ;
default : // extension handling, in particular ufrag
candidate [ parts [ i ] ] = parts [ i + 1 ] ;
break ;
}
}
return candidate ;
} ;
// Translates a candidate object into SDP candidate attribute.
SDPUtils . writeCandidate = function ( candidate ) {
var sdp = [ ] ;
sdp . push ( candidate . foundation ) ;
sdp . push ( candidate . component ) ;
sdp . push ( candidate . protocol . toUpperCase ( ) ) ;
sdp . push ( candidate . priority ) ;
sdp . push ( candidate . address || candidate . ip ) ;
sdp . push ( candidate . port ) ;
var type = candidate . type ;
sdp . push ( 'typ' ) ;
sdp . push ( type ) ;
if ( type !== 'host' && candidate . relatedAddress &&
candidate . relatedPort ) {
sdp . push ( 'raddr' ) ;
sdp . push ( candidate . relatedAddress ) ;
sdp . push ( 'rport' ) ;
sdp . push ( candidate . relatedPort ) ;
}
if ( candidate . tcpType && candidate . protocol . toLowerCase ( ) === 'tcp' ) {
sdp . push ( 'tcptype' ) ;
sdp . push ( candidate . tcpType ) ;
}
if ( candidate . usernameFragment || candidate . ufrag ) {
sdp . push ( 'ufrag' ) ;
sdp . push ( candidate . usernameFragment || candidate . ufrag ) ;
}
return 'candidate:' + sdp . join ( ' ' ) ;
} ;
// Parses an ice-options line, returns an array of option tags.
// a=ice-options:foo bar
SDPUtils . parseIceOptions = function ( line ) {
return line . substr ( 14 ) . split ( ' ' ) ;
} ;
// Parses an rtpmap line, returns RTCRtpCoddecParameters. Sample input:
// a=rtpmap:111 opus/48000/2
SDPUtils . parseRtpMap = function ( line ) {
var parts = line . substr ( 9 ) . split ( ' ' ) ;
var parsed = {
payloadType : parseInt ( parts . shift ( ) , 10 ) // was: id
} ;
parts = parts [ 0 ] . split ( '/' ) ;
parsed . name = parts [ 0 ] ;
parsed . clockRate = parseInt ( parts [ 1 ] , 10 ) ; // was: clockrate
parsed . channels = parts . length === 3 ? parseInt ( parts [ 2 ] , 10 ) : 1 ;
// legacy alias, got renamed back to channels in ORTC.
parsed . numChannels = parsed . channels ;
return parsed ;
} ;
// Generate an a=rtpmap line from RTCRtpCodecCapability or
// RTCRtpCodecParameters.
SDPUtils . writeRtpMap = function ( codec ) {
var pt = codec . payloadType ;
if ( codec . preferredPayloadType !== undefined ) {
pt = codec . preferredPayloadType ;
}
var channels = codec . channels || codec . numChannels || 1 ;
return 'a=rtpmap:' + pt + ' ' + codec . name + '/' + codec . clockRate +
( channels !== 1 ? '/' + channels : '' ) + '\r\n' ;
} ;
// Parses an a=extmap line (headerextension from RFC 5285). Sample input:
// a=extmap:2 urn:ietf:params:rtp-hdrext:toffset
// a=extmap:2/sendonly urn:ietf:params:rtp-hdrext:toffset
SDPUtils . parseExtmap = function ( line ) {
var parts = line . substr ( 9 ) . split ( ' ' ) ;
return {
id : parseInt ( parts [ 0 ] , 10 ) ,
direction : parts [ 0 ] . indexOf ( '/' ) > 0 ? parts [ 0 ] . split ( '/' ) [ 1 ] : 'sendrecv' ,
uri : parts [ 1 ]
} ;
} ;
// Generates a=extmap line from RTCRtpHeaderExtensionParameters or
// RTCRtpHeaderExtension.
SDPUtils . writeExtmap = function ( headerExtension ) {
return 'a=extmap:' + ( headerExtension . id || headerExtension . preferredId ) +
( headerExtension . direction && headerExtension . direction !== 'sendrecv'
? '/' + headerExtension . direction
: '' ) +
' ' + headerExtension . uri + '\r\n' ;
} ;
// Parses an ftmp line, returns dictionary. Sample input:
// a=fmtp:96 vbr=on;cng=on
// Also deals with vbr=on; cng=on
SDPUtils . parseFmtp = function ( line ) {
var parsed = { } ;
var kv ;
var parts = line . substr ( line . indexOf ( ' ' ) + 1 ) . split ( ';' ) ;
for ( var j = 0 ; j < parts . length ; j ++ ) {
kv = parts [ j ] . trim ( ) . split ( '=' ) ;
parsed [ kv [ 0 ] . trim ( ) ] = kv [ 1 ] ;
}
return parsed ;
} ;
// Generates an a=ftmp line from RTCRtpCodecCapability or RTCRtpCodecParameters.
SDPUtils . writeFmtp = function ( codec ) {
var line = '' ;
var pt = codec . payloadType ;
if ( codec . preferredPayloadType !== undefined ) {
pt = codec . preferredPayloadType ;
}
if ( codec . parameters && Object . keys ( codec . parameters ) . length ) {
var params = [ ] ;
Object . keys ( codec . parameters ) . forEach ( function ( param ) {
if ( codec . parameters [ param ] ) {
params . push ( param + '=' + codec . parameters [ param ] ) ;
} else {
params . push ( param ) ;
}
} ) ;
line += 'a=fmtp:' + pt + ' ' + params . join ( ';' ) + '\r\n' ;
}
return line ;
} ;
// Parses an rtcp-fb line, returns RTCPRtcpFeedback object. Sample input:
// a=rtcp-fb:98 nack rpsi
SDPUtils . parseRtcpFb = function ( line ) {
var parts = line . substr ( line . indexOf ( ' ' ) + 1 ) . split ( ' ' ) ;
return {
type : parts . shift ( ) ,
parameter : parts . join ( ' ' )
} ;
} ;
// Generate a=rtcp-fb lines from RTCRtpCodecCapability or RTCRtpCodecParameters.
SDPUtils . writeRtcpFb = function ( codec ) {
var lines = '' ;
var pt = codec . payloadType ;
if ( codec . preferredPayloadType !== undefined ) {
pt = codec . preferredPayloadType ;
}
if ( codec . rtcpFeedback && codec . rtcpFeedback . length ) {
// FIXME: special handling for trr-int?
codec . rtcpFeedback . forEach ( function ( fb ) {
lines += 'a=rtcp-fb:' + pt + ' ' + fb . type +
( fb . parameter && fb . parameter . length ? ' ' + fb . parameter : '' ) +
'\r\n' ;
} ) ;
}
return lines ;
} ;
// Parses an RFC 5576 ssrc media attribute. Sample input:
// a=ssrc:3735928559 cname:something
SDPUtils . parseSsrcMedia = function ( line ) {
var sp = line . indexOf ( ' ' ) ;
var parts = {
ssrc : parseInt ( line . substr ( 7 , sp - 7 ) , 10 )
} ;
var colon = line . indexOf ( ':' , sp ) ;
if ( colon > - 1 ) {
parts . attribute = line . substr ( sp + 1 , colon - sp - 1 ) ;
parts . value = line . substr ( colon + 1 ) ;
} else {
parts . attribute = line . substr ( sp + 1 ) ;
}
return parts ;
} ;
SDPUtils . parseSsrcGroup = function ( line ) {
var parts = line . substr ( 13 ) . split ( ' ' ) ;
return {
semantics : parts . shift ( ) ,
ssrcs : parts . map ( function ( ssrc ) {
return parseInt ( ssrc , 10 ) ;
} )
} ;
} ;
// Extracts the MID (RFC 5888) from a media section.
// returns the MID or undefined if no mid line was found.
SDPUtils . getMid = function ( mediaSection ) {
var mid = SDPUtils . matchPrefix ( mediaSection , 'a=mid:' ) [ 0 ] ;
if ( mid ) {
return mid . substr ( 6 ) ;
}
} ;
SDPUtils . parseFingerprint = function ( line ) {
var parts = line . substr ( 14 ) . split ( ' ' ) ;
return {
algorithm : parts [ 0 ] . toLowerCase ( ) , // algorithm is case-sensitive in Edge.
value : parts [ 1 ]
} ;
} ;
// Extracts DTLS parameters from SDP media section or sessionpart.
// FIXME: for consistency with other functions this should only
// get the fingerprint line as input. See also getIceParameters.
SDPUtils . getDtlsParameters = function ( mediaSection , sessionpart ) {
var lines = SDPUtils . matchPrefix ( mediaSection + sessionpart ,
'a=fingerprint:' ) ;
// Note: a=setup line is ignored since we use the 'auto' role.
// Note2: 'algorithm' is not case sensitive except in Edge.
return {
role : 'auto' ,
fingerprints : lines . map ( SDPUtils . parseFingerprint )
} ;
} ;
// Serializes DTLS parameters to SDP.
SDPUtils . writeDtlsParameters = function ( params , setupType ) {
var sdp = 'a=setup:' + setupType + '\r\n' ;
params . fingerprints . forEach ( function ( fp ) {
sdp += 'a=fingerprint:' + fp . algorithm + ' ' + fp . value + '\r\n' ;
} ) ;
return sdp ;
} ;
// Parses ICE information from SDP media section or sessionpart.
// FIXME: for consistency with other functions this should only
// get the ice-ufrag and ice-pwd lines as input.
SDPUtils . getIceParameters = function ( mediaSection , sessionpart ) {
var lines = SDPUtils . splitLines ( mediaSection ) ;
// Search in session part, too.
lines = lines . concat ( SDPUtils . splitLines ( sessionpart ) ) ;
var iceParameters = {
usernameFragment : lines . filter ( function ( line ) {
return line . indexOf ( 'a=ice-ufrag:' ) === 0 ;
} ) [ 0 ] . substr ( 12 ) ,
password : lines . filter ( function ( line ) {
return line . indexOf ( 'a=ice-pwd:' ) === 0 ;
} ) [ 0 ] . substr ( 10 )
} ;
return iceParameters ;
} ;
// Serializes ICE parameters to SDP.
SDPUtils . writeIceParameters = function ( params ) {
return 'a=ice-ufrag:' + params . usernameFragment + '\r\n' +
'a=ice-pwd:' + params . password + '\r\n' ;
} ;
// Parses the SDP media section and returns RTCRtpParameters.
SDPUtils . parseRtpParameters = function ( mediaSection ) {
var description = {
codecs : [ ] ,
headerExtensions : [ ] ,
fecMechanisms : [ ] ,
rtcp : [ ]
} ;
var lines = SDPUtils . splitLines ( mediaSection ) ;
var mline = lines [ 0 ] . split ( ' ' ) ;
for ( var i = 3 ; i < mline . length ; i ++ ) { // find all codecs from mline[3..]
var pt = mline [ i ] ;
var rtpmapline = SDPUtils . matchPrefix (
mediaSection , 'a=rtpmap:' + pt + ' ' ) [ 0 ] ;
if ( rtpmapline ) {
var codec = SDPUtils . parseRtpMap ( rtpmapline ) ;
var fmtps = SDPUtils . matchPrefix (
mediaSection , 'a=fmtp:' + pt + ' ' ) ;
// Only the first a=fmtp:<pt> is considered.
codec . parameters = fmtps . length ? SDPUtils . parseFmtp ( fmtps [ 0 ] ) : { } ;
codec . rtcpFeedback = SDPUtils . matchPrefix (
mediaSection , 'a=rtcp-fb:' + pt + ' ' )
. map ( SDPUtils . parseRtcpFb ) ;
description . codecs . push ( codec ) ;
// parse FEC mechanisms from rtpmap lines.
switch ( codec . name . toUpperCase ( ) ) {
case 'RED' :
case 'ULPFEC' :
description . fecMechanisms . push ( codec . name . toUpperCase ( ) ) ;
break ;
default : // only RED and ULPFEC are recognized as FEC mechanisms.
break ;
}
}
}
SDPUtils . matchPrefix ( mediaSection , 'a=extmap:' ) . forEach ( function ( line ) {
description . headerExtensions . push ( SDPUtils . parseExtmap ( line ) ) ;
} ) ;
// FIXME: parse rtcp.
return description ;
} ;
// Generates parts of the SDP media section describing the capabilities /
// parameters.
SDPUtils . writeRtpDescription = function ( kind , caps ) {
var sdp = '' ;
// Build the mline.
sdp += 'm=' + kind + ' ' ;
sdp += caps . codecs . length > 0 ? '9' : '0' ; // reject if no codecs.
sdp += ' UDP/TLS/RTP/SAVPF ' ;
sdp += caps . codecs . map ( function ( codec ) {
if ( codec . preferredPayloadType !== undefined ) {
return codec . preferredPayloadType ;
}
return codec . payloadType ;
} ) . join ( ' ' ) + '\r\n' ;
sdp += 'c=IN IP4 0.0.0.0\r\n' ;
sdp += 'a=rtcp:9 IN IP4 0.0.0.0\r\n' ;
// Add a=rtpmap lines for each codec. Also fmtp and rtcp-fb.
caps . codecs . forEach ( function ( codec ) {
sdp += SDPUtils . writeRtpMap ( codec ) ;
sdp += SDPUtils . writeFmtp ( codec ) ;
sdp += SDPUtils . writeRtcpFb ( codec ) ;
} ) ;
var maxptime = 0 ;
caps . codecs . forEach ( function ( codec ) {
if ( codec . maxptime > maxptime ) {
maxptime = codec . maxptime ;
}
} ) ;
if ( maxptime > 0 ) {
sdp += 'a=maxptime:' + maxptime + '\r\n' ;
}
sdp += 'a=rtcp-mux\r\n' ;
if ( caps . headerExtensions ) {
caps . headerExtensions . forEach ( function ( extension ) {
sdp += SDPUtils . writeExtmap ( extension ) ;
} ) ;
}
// FIXME: write fecMechanisms.
return sdp ;
} ;
// 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 . substr ( 17 ) . split ( ' ' ) ;
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 )
} ;
if ( primarySsrc && secondarySsrc ) {
encParam . rtx = { ssrc : secondarySsrc } ;
}
encodingParameters . push ( encParam ) ;
if ( hasRed ) {
encParam = JSON . parse ( JSON . stringify ( encParam ) ) ;
encParam . fec = {
ssrc : primarySsrc ,
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 ) {
// use formula from JSEP to convert b=AS to TIAS value.
bandwidth = parseInt ( bandwidth [ 0 ] . substr ( 5 ) , 10 ) * 1000 * 0.95
- ( 50 * 40 * 8 ) ;
} else {
bandwidth = undefined ;
}
encodingParameters . forEach ( function ( params ) {
params . maxBitrate = bandwidth ;
} ) ;
}
return encodingParameters ;
} ;
// parses http://draft.ortc.org/#rtcrtcpparameters*
SDPUtils . parseRtcpParameters = function ( mediaSection ) {
var rtcpParameters = { } ;
// Gets the first SSRC. Note tha 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 ) {
rtcpParameters . cname = remoteSsrc . value ;
rtcpParameters . ssrc = remoteSsrc . ssrc ;
}
// Edge uses the compound attribute instead of reducedSize
// compound is !reducedSize
var rsize = SDPUtils . matchPrefix ( mediaSection , 'a=rtcp-rsize' ) ;
rtcpParameters . reducedSize = rsize . length > 0 ;
rtcpParameters . compound = rsize . length === 0 ;
// parses the rtcp-mux attrі bute.
// Note that Edge does not support unmuxed RTCP.
var mux = SDPUtils . matchPrefix ( mediaSection , 'a=rtcp-mux' ) ;
rtcpParameters . mux = mux . length > 0 ;
return rtcpParameters ;
} ;
// parses either a=msid: or a=ssrc:... msid lines and returns
// the id of the MediaStream and MediaStreamTrack.
SDPUtils . parseMsid = function ( mediaSection ) {
var parts ;
var spec = SDPUtils . matchPrefix ( mediaSection , 'a=msid:' ) ;
if ( spec . length === 1 ) {
parts = spec [ 0 ] . substr ( 7 ) . split ( ' ' ) ;
return { stream : parts [ 0 ] , track : parts [ 1 ] } ;
}
var planB = SDPUtils . matchPrefix ( mediaSection , 'a=ssrc:' )
. map ( function ( line ) {
return SDPUtils . parseSsrcMedia ( line ) ;
} )
. filter ( function ( msidParts ) {
return msidParts . attribute === 'msid' ;
} ) ;
if ( planB . length > 0 ) {
parts = planB [ 0 ] . value . split ( ' ' ) ;
return { stream : parts [ 0 ] , track : parts [ 1 ] } ;
}
} ;
// Generate a session ID for SDP.
// https://tools.ietf.org/html/draft-ietf-rtcweb-jsep-20#section-5.2.1
// recommends using a cryptographically random +ve 64-bit value
// but right now this should be acceptable and within the right range
SDPUtils . generateSessionId = function ( ) {
return Math . random ( ) . toString ( ) . substr ( 2 , 21 ) ;
} ;
// Write boilder plate for start of SDP
// sessId argument is optional - if not supplied it will
// be generated randomly
// sessVersion is optional and defaults to 2
// sessUser is optional and defaults to 'thisisadapterortc'
SDPUtils . writeSessionBoilerplate = function ( sessId , sessVer , sessUser ) {
var sessionId ;
var version = sessVer !== undefined ? sessVer : 2 ;
if ( sessId ) {
sessionId = sessId ;
} else {
sessionId = SDPUtils . generateSessionId ( ) ;
}
var user = sessUser || 'thisisadapterortc' ;
// FIXME: sess-id should be an NTP timestamp.
return 'v=0\r\n' +
'o=' + user + ' ' + sessionId + ' ' + version +
' IN IP4 127.0.0.1\r\n' +
's=-\r\n' +
't=0 0\r\n' ;
} ;
SDPUtils . writeMediaSection = function ( transceiver , caps , type , stream ) {
var sdp = SDPUtils . writeRtpDescription ( transceiver . kind , caps ) ;
// Map ICE parameters (ufrag, pwd) to SDP.
sdp += SDPUtils . writeIceParameters (
transceiver . iceGatherer . getLocalParameters ( ) ) ;
// Map DTLS parameters to SDP.
sdp += SDPUtils . writeDtlsParameters (
transceiver . dtlsTransport . getLocalParameters ( ) ,
type === 'offer' ? 'actpass' : 'active' ) ;
sdp += 'a=mid:' + transceiver . mid + '\r\n' ;
if ( transceiver . direction ) {
sdp += 'a=' + transceiver . direction + '\r\n' ;
} else if ( transceiver . rtpSender && transceiver . rtpReceiver ) {
sdp += 'a=sendrecv\r\n' ;
} else if ( transceiver . rtpSender ) {
sdp += 'a=sendonly\r\n' ;
} else if ( transceiver . rtpReceiver ) {
sdp += 'a=recvonly\r\n' ;
} else {
sdp += 'a=inactive\r\n' ;
}
if ( transceiver . rtpSender ) {
// spec.
var msid = 'msid:' + stream . id + ' ' +
transceiver . rtpSender . track . id + '\r\n' ;
sdp += 'a=' + msid ;
// for Chrome.
sdp += 'a=ssrc:' + transceiver . sendEncodingParameters [ 0 ] . ssrc +
' ' + msid ;
if ( transceiver . sendEncodingParameters [ 0 ] . rtx ) {
sdp += 'a=ssrc:' + transceiver . sendEncodingParameters [ 0 ] . rtx . ssrc +
' ' + msid ;
sdp += 'a=ssrc-group:FID ' +
transceiver . sendEncodingParameters [ 0 ] . ssrc + ' ' +
transceiver . sendEncodingParameters [ 0 ] . rtx . ssrc +
'\r\n' ;
}
}
// FIXME: this should be written by writeRtpDescription.
sdp += 'a=ssrc:' + transceiver . sendEncodingParameters [ 0 ] . ssrc +
' cname:' + SDPUtils . localCName + '\r\n' ;
if ( transceiver . rtpSender && transceiver . sendEncodingParameters [ 0 ] . rtx ) {
sdp += 'a=ssrc:' + transceiver . sendEncodingParameters [ 0 ] . rtx . ssrc +
' cname:' + SDPUtils . localCName + '\r\n' ;
}
return sdp ;
} ;
// Gets the direction from the mediaSection or the sessionpart.
SDPUtils . getDirection = function ( mediaSection , sessionpart ) {
// Look for sendrecv, sendonly, recvonly, inactive, default to sendrecv.
var lines = SDPUtils . splitLines ( mediaSection ) ;
for ( var i = 0 ; i < lines . length ; i ++ ) {
switch ( lines [ i ] ) {
case 'a=sendrecv' :
case 'a=sendonly' :
case 'a=recvonly' :
case 'a=inactive' :
return lines [ i ] . substr ( 2 ) ;
default :
// FIXME: What should happen here?
}
}
if ( sessionpart ) {
return SDPUtils . getDirection ( sessionpart ) ;
}
return 'sendrecv' ;
} ;
SDPUtils . getKind = function ( mediaSection ) {
var lines = SDPUtils . splitLines ( mediaSection ) ;
var mline = lines [ 0 ] . split ( ' ' ) ;
return mline [ 0 ] . substr ( 2 ) ;
} ;
SDPUtils . isRejected = function ( mediaSection ) {
return mediaSection . split ( ' ' , 2 ) [ 1 ] === '0' ;
} ;
SDPUtils . parseMLine = function ( mediaSection ) {
var lines = SDPUtils . splitLines ( mediaSection ) ;
var parts = lines [ 0 ] . substr ( 2 ) . split ( ' ' ) ;
return {
kind : parts [ 0 ] ,
port : parseInt ( parts [ 1 ] , 10 ) ,
protocol : parts [ 2 ] ,
fmt : parts . slice ( 3 ) . join ( ' ' )
} ;
} ;
SDPUtils . parseOLine = function ( mediaSection ) {
var line = SDPUtils . matchPrefix ( mediaSection , 'o=' ) [ 0 ] ;
var parts = line . substr ( 2 ) . split ( ' ' ) ;
return {
username : parts [ 0 ] ,
sessionId : parts [ 1 ] ,
sessionVersion : parseInt ( parts [ 2 ] , 10 ) ,
netType : parts [ 3 ] ,
addressType : parts [ 4 ] ,
address : parts [ 5 ]
} ;
} ;
// a very naive interpretation of a valid SDP.
SDPUtils . isValidSDP = function ( blob ) {
if ( typeof blob !== 'string' || blob . length === 0 ) {
return false ;
}
var lines = SDPUtils . splitLines ( blob ) ;
for ( var i = 0 ; i < lines . length ; i ++ ) {
if ( lines [ i ] . length < 2 || lines [ i ] . charAt ( 1 ) !== '=' ) {
return false ;
}
// TODO: check the modifier a bit more.
}
return true ;
} ;
// Expose public methods.
if ( typeof module === 'object' ) {
module . exports = SDPUtils ;
}
} , { } ] } , { } , [ 1 ] ) ( 1 )
} ) ;