Comparing version 0.9.2 to 0.10.0
{ | ||
"name": "sip.js", | ||
"version": "0.9.2", | ||
"version": "0.10.0", | ||
"authors": [ | ||
@@ -5,0 +5,0 @@ "James Criscuolo <james@onsip.com>", |
@@ -41,5 +41,4 @@ ## What you need to build SIP.js | ||
The built version of SIP.js will be available in the `dist/` subdirectory in both flavors: normal (uncompressed) and minified, both linted with [JSLint](http://jslint.com/). There are copies of each file with the version number in the title in that subdirectory as well | ||
The built version of SIP.js will be available in the `dist/` subdirectory in both flavors: normal (uncompressed) and minified, both linted with [ESLint](https://eslint.org/). There are copies of each file with the version number in the title in that subdirectory as well. | ||
## Development version | ||
@@ -46,0 +45,0 @@ |
@@ -5,3 +5,3 @@ { | ||
"description": "A simple, intuitive, and powerful JavaScript signaling library", | ||
"version": "0.9.2", | ||
"version": "0.10.0", | ||
"main": "dist/sip.min.js", | ||
@@ -8,0 +8,0 @@ "browser": { |
@@ -198,4 +198,9 @@ "use strict"; | ||
uui: true // RFC 7433 | ||
}, | ||
dtmfType: { | ||
INFO: 'info', | ||
RTP: 'rtp' | ||
} | ||
}; | ||
}; |
@@ -13,3 +13,3 @@ "use strict"; | ||
module.exports = function (EventEmitter) { | ||
var SessionDescriptionHandler = function(session, options) {}; | ||
var SessionDescriptionHandler = function(session, observer, options) {}; | ||
@@ -52,3 +52,17 @@ SessionDescriptionHandler.prototype = Object.create(EventEmitter.prototype, { | ||
*/ | ||
setDescription: {value: function setDescription (sessionDescription, options, modifiers) {}} | ||
setDescription: {value: function setDescription (sessionDescription, options, modifiers) {}}, | ||
/** | ||
* Send DTMF via RTP (RFC 4733) | ||
* @param {String} tones A string containing DTMF digits | ||
* @param {Object} [options] Options object to be used by sendDtmf | ||
* @returns {boolean} true if DTMF send is successful, false otherwise | ||
*/ | ||
sendDtmf: {value: function sendDtmf (tones, options) {}}, | ||
/** | ||
* Get the direction of the session description | ||
* @returns {String} direction of the description | ||
*/ | ||
getDirection: {value: function getDirection() {}}, | ||
}); | ||
@@ -55,0 +69,0 @@ |
@@ -167,2 +167,7 @@ "use strict"; | ||
if (!this.ws) { | ||
transport.onError('Websocket could not be instantiated.'); | ||
return; | ||
} | ||
this.ws.binaryType = 'arraybuffer'; | ||
@@ -169,0 +174,0 @@ |
@@ -979,2 +979,7 @@ "use strict"; | ||
// DTMF type: 'info' or 'rtp' (RFC 4733) | ||
// RTP Payload Spec: https://tools.ietf.org/html/rfc4733 | ||
// WebRTC Audio Spec: https://tools.ietf.org/html/rfc7874 | ||
dtmfType: SIP.C.dtmfType.INFO, | ||
// Replaces header (RFC 3891) | ||
@@ -1297,2 +1302,13 @@ // http://tools.ietf.org/html/rfc3891 | ||
dtmfType: function(dtmfType) { | ||
switch (dtmfType) { | ||
case SIP.C.dtmfType.RTP: | ||
return SIP.C.dtmfType.RTP; | ||
case SIP.C.dtmfType.INFO: | ||
// Fall through | ||
default: | ||
return SIP.C.dtmfType.INFO; | ||
} | ||
}, | ||
hackViaTcp: function(hackViaTcp) { | ||
@@ -1299,0 +1315,0 @@ if (typeof hackViaTcp === 'boolean') { |
@@ -237,3 +237,3 @@ "use strict"; | ||
reason = SIP.Utils.getReasonPhrase(code, reason); | ||
return 'SIP ;cause=' + code + ' ;text="' + reason + '"'; | ||
return 'SIP;cause=' + code + ';text="' + reason + '"'; | ||
}, | ||
@@ -240,0 +240,0 @@ |
@@ -14,3 +14,3 @@ "use strict"; | ||
// Constructor | ||
var SessionDescriptionHandler = function(session, options) { | ||
var SessionDescriptionHandler = function(session, observer, options) { | ||
// TODO: Validate the options | ||
@@ -21,5 +21,20 @@ this.options = options || {}; | ||
this.session = session; | ||
this.observer = observer; | ||
this.dtmfSender = null; | ||
this.CONTENT_TYPE = 'application/sdp'; | ||
this.C = {}; | ||
this.C.DIRECTION = { | ||
NULL: null, | ||
SENDRECV: "sendrecv", | ||
SENDONLY: "sendonly", | ||
RECVONLY: "recvonly", | ||
INACTIVE: "inactive" | ||
}; | ||
this.logger.log('SessionDescriptionHandlerOptions: ' + JSON.stringify(this.options)); | ||
this.direction = this.C.DIRECTION.NULL; | ||
this.modifiers = this.options.modifiers || []; | ||
@@ -45,4 +60,2 @@ if (!Array.isArray(this.modifiers)) { | ||
this.constraints = this.checkAndDefaultConstraints(this.options.constraints); | ||
this.session.emit('SessionDescriptionHandler-created', this); | ||
}; | ||
@@ -55,4 +68,4 @@ | ||
SessionDescriptionHandler.defaultFactory = function defaultFactory (session, options) { | ||
return new SessionDescriptionHandler(session, options); | ||
SessionDescriptionHandler.defaultFactory = function defaultFactory (session, observer, options) { | ||
return new SessionDescriptionHandler(session, observer, options); | ||
}; | ||
@@ -289,2 +302,46 @@ | ||
/** | ||
* Send DTMF via RTP (RFC 4733) | ||
* @param {String} tones A string containing DTMF digits | ||
* @param {Object} [options] Options object to be used by sendDtmf | ||
* @returns {boolean} true if DTMF send is successful, false otherwise | ||
*/ | ||
sendDtmf: {writable: true, value: function sendDtmf (tones, options) { | ||
if (!this.dtmfSender && this.hasBrowserGetSenderSupport()) { | ||
var senders = this.peerConnection.getSenders(); | ||
if (senders.length > 0) { | ||
this.dtmfSender = senders[0].dtmf; | ||
} | ||
} | ||
if (!this.dtmfSender && this.hasBrowserTrackSupport()) { | ||
var streams = this.peerConnection.getLocalStreams(); | ||
if (streams.length > 0) { | ||
var audioTracks = streams[0].getAudioTracks(); | ||
if (audioTracks.length > 0) { | ||
this.dtmfSender = this.peerConnection.createDTMFSender(audioTracks[0]); | ||
} | ||
} | ||
} | ||
if (!this.dtmfSender) { | ||
return false; | ||
} | ||
try { | ||
this.dtmfSender.insertDTMF(tones, options.duration, options.interToneGap); | ||
} | ||
catch (e) { | ||
if (e.type === "InvalidStateError" || e.type === "InvalidCharacterError") { | ||
this.logger.error(e); | ||
return false; | ||
} else { | ||
throw e; | ||
} | ||
} | ||
this.logger.log('DTMF sent via RTP: ' + tones.toString()); | ||
return true; | ||
}}, | ||
getDirection: {writable: true, value: function getDirection() { | ||
return this.direction; | ||
}}, | ||
// Internal functions | ||
@@ -306,6 +363,6 @@ createOfferOrAnswer: {writable: true, value: function createOfferOrAnswer (RTCOfferOptions, modifiers) { | ||
.then(function(sdp) { | ||
return SIP.Utils.reducePromises(modifiers, sdp); | ||
return SIP.Utils.reducePromises(modifiers, self.createRTCSessionDescriptionInit(sdp)); | ||
}) | ||
.then(function(sdp) { | ||
self.logger.log(sdp); | ||
self.resetIceGatheringComplete(); | ||
return pc.setLocalDescription(sdp); | ||
@@ -321,3 +378,3 @@ }) | ||
.then(function readySuccess() { | ||
var localDescription = self.peerConnection.localDescription; | ||
var localDescription = self.createRTCSessionDescriptionInit(self.peerConnection.localDescription); | ||
return SIP.Utils.reducePromises(modifiers, localDescription); | ||
@@ -327,2 +384,3 @@ }) | ||
self.emit('getDescription', localDescription); | ||
self.setDirection(localDescription.sdp); | ||
return localDescription.sdp; | ||
@@ -337,2 +395,10 @@ }) | ||
// Creates an RTCSessionDescriptionInit from an RTCSessionDescription | ||
createRTCSessionDescriptionInit: {writable: true, value: function createRTCSessionDescriptionInit(RTCSessionDescription) { | ||
return { | ||
type: RTCSessionDescription.type, | ||
sdp: RTCSessionDescription.sdp | ||
}; | ||
}}, | ||
addDefaultIceCheckingTimeout: {writable: true, value: function addDefaultIceCheckingTimeout (peerConnectionOptions) { | ||
@@ -362,2 +428,10 @@ if (peerConnectionOptions.iceCheckingTimeout === undefined) { | ||
hasBrowserTrackSupport: {writable: true, value: function hasBrowserTrackSupport () { | ||
return Boolean(this.peerConnection.addTrack); | ||
}}, | ||
hasBrowserGetSenderSupport: {writable: true, value: function hasBrowserGetSenderSupport () { | ||
return Boolean(this.peerConnection.getSenders); | ||
}}, | ||
initPeerConnection: {writable: true, value: function initPeerConnection(options) { | ||
@@ -383,18 +457,16 @@ var self = this; | ||
this.peerConnection.ontrack = function(e) { | ||
self.logger.log('track added'); | ||
self.emit('addTrack', e); | ||
}; | ||
if ('ontrack' in this.peerConnection) { | ||
this.peerConnection.addEventListener('track', function(e) { | ||
self.logger.log('track added'); | ||
self.observer.trackAdded(); | ||
self.emit('addTrack', e); | ||
}); | ||
} else { | ||
this.logger.warn('Using onaddstream which is deprecated'); | ||
this.peerConnection.onaddstream = function(e) { | ||
self.logger.log('stream added'); | ||
self.emit('addStream', e); | ||
}; | ||
} | ||
this.peerConnection.onaddstream = function(e) { | ||
self.logger.warn('Using deprecated stream API'); | ||
self.logger.log('stream added'); | ||
self.emit('addStream', e); | ||
}; | ||
// TODO: There is no remove track listener | ||
this.peerConnection.onremovestream = function(e) { | ||
self.logger.log('stream removed: '+ e.stream.id); | ||
}; | ||
this.peerConnection.onicecandidate = function(e) { | ||
@@ -471,19 +543,12 @@ self.emit('iceCandidate', e); | ||
var emitThenCall = function(eventName, callback) { | ||
var callbackArgs = Array.prototype.slice.call(arguments, 2); | ||
// Emit with all of the arguments from the real callback. | ||
var newArgs = [eventName].concat(callbackArgs); | ||
this.emit.apply(this, newArgs); | ||
return callback.apply(null, callbackArgs); | ||
}.bind(this); | ||
if (constraints.audio || constraints.video) { | ||
this.WebRTC.getUserMedia(constraints) | ||
.then( | ||
emitThenCall.bind(this, 'userMedia', function(streams) { resolve(streams); }), | ||
emitThenCall.bind(this, 'userMediaFailed', function(e) { | ||
reject(e); | ||
throw e; | ||
}) | ||
); | ||
.then(function(streams) { | ||
this.observer.trackAdded(); | ||
this.emit('userMedia', streams); | ||
resolve(streams); | ||
}.bind(this)).catch(function(e) { | ||
this.emit('userMediaFailed', e); | ||
reject(e); | ||
}.bind(this)); | ||
} else { | ||
@@ -514,3 +579,3 @@ // Local streams were explicitly excluded. | ||
resetIceGatheringComplete: {writable: true, value: function rejectIceGatheringComplete() { | ||
resetIceGatheringComplete: {writable: true, value: function resetIceGatheringComplete() { | ||
this.iceGatheringTimeout = false; | ||
@@ -529,2 +594,24 @@ | ||
setDirection: {writable: true, value: function setDirection(sdp) { | ||
var match = sdp.match(/a=(sendrecv|sendonly|recvonly|inactive)/); | ||
if (match === null) { | ||
this.direction = this.C.DIRECTION.NULL; | ||
this.observer.directionChanged(); | ||
return; | ||
} | ||
var direction = match[1]; | ||
switch (direction) { | ||
case this.C.DIRECTION.SENDRECV: | ||
case this.C.DIRECTION.SENDONLY: | ||
case this.C.DIRECTION.RECVONLY: | ||
case this.C.DIRECTION.INACTIVE: | ||
this.direction = direction; | ||
break; | ||
default: | ||
this.direction = this.C.DIRECTION.NULL; | ||
break; | ||
} | ||
this.observer.directionChanged(); | ||
}}, | ||
triggerIceGatheringComplete: {writable: true, value: function triggerIceGatheringComplete() { | ||
@@ -531,0 +618,0 @@ if (this.isIceGatheringComplete()) { |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
1563806
53
30009