Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

twilio-video

Package Overview
Dependencies
Maintainers
1
Versions
123
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

twilio-video - npm Package Compare versions

Comparing version 2.0.0-beta2 to 2.0.0-beta3

16

CHANGELOG.md

@@ -0,1 +1,17 @@

2.0.0-beta3 (November 20, 2018)
===============================
Bug Fixes
---------
- Fixed a bug where unpublishing a LocalTrack from within one of its event
listeners that have been added before publishing it to the Room throws a
TypeError. (JSDK-2212)
Note for Electron developers
----------------------------
- twilio-video.js will no longer be usable on Electron 2.x or below. Please
upgrade to Electron 3.x or higher.
2.0.0-beta2 (October 1, 2018)

@@ -2,0 +18,0 @@ =============================

8

es5/connect.js

@@ -373,5 +373,7 @@ 'use strict';

log.debug('Creating a new Room:', room);
roomSignaling.on('stateChanged', function stateChanged() {
log.info('Disconnected from Room:', room.toString());
roomSignaling.removeListener('stateChanged', stateChanged);
roomSignaling.on('stateChanged', function stateChanged(state) {
if (state === 'disconnected') {
log.info('Disconnected from Room:', room.toString());
roomSignaling.removeListener('stateChanged', stateChanged);
}
});

@@ -378,0 +380,0 @@

@@ -195,4 +195,6 @@ 'use strict';

var trackSignaling = signaling.getPublication(localTrack._trackSender);
trackSignaling.disable();
log.debug('Disabled the ' + util.trackClass(localTrack, true) + ':', localTrack.id);
if (trackSignaling) {
trackSignaling.disable();
log.debug('Disabled the ' + util.trackClass(localTrack, true) + ':', localTrack.id);
}
}

@@ -202,4 +204,6 @@

var trackSignaling = signaling.getPublication(localTrack._trackSender);
trackSignaling.enable();
log.debug('Enabled the ' + util.trackClass(localTrack, true) + ':', localTrack.id);
if (trackSignaling) {
trackSignaling.enable();
log.debug('Enabled the ' + util.trackClass(localTrack, true) + ':', localTrack.id);
}
}

@@ -211,3 +215,5 @@

var trackSignaling = signaling.getPublication(localTrack._trackSender);
trackSignaling.stop();
if (trackSignaling) {
trackSignaling.stop();
}
}

@@ -214,0 +220,0 @@

@@ -62,2 +62,3 @@ 'use strict';

environment: options.environment,
logLevel: options.logLevel,
networkQuality: options.networkQuality,

@@ -64,0 +65,0 @@ iceServerSourceStatus: iceServerSource.status,

@@ -119,7 +119,8 @@ 'use strict';

var sdpFormat = getSdpFormat(options.sdpSemantics);
var isUnifiedPlan = sdpFormat === 'unified';
var localMediaStream = isFirefox && RTCPeerConnection.prototype.addTransceiver ? null : new options.MediaStream();
var localMediaStream = isUnifiedPlan && RTCPeerConnection.prototype.addTransceiver ? null : new options.MediaStream();
if (options.dummyAudioMediaStreamTrack) {
peerConnection.addTrack(options.dummyAudioMediaStreamTrack, localMediaStream || new MediaStream());
peerConnection.addTrack(options.dummyAudioMediaStreamTrack, localMediaStream || new options.MediaStream());
}

@@ -153,2 +154,5 @@

},
_isUnifiedPlan: {
value: isUnifiedPlan
},
_lastIceConnectionState: {

@@ -532,3 +536,3 @@ writable: true,

// the same technique as Firefox.
: isSafari || this._sdpFormat === 'unified' ? new OrderedTrackMatcher() : new IdentityTrackMatcher();
: isSafari || this._isUnifiedPlan ? new OrderedTrackMatcher() : new IdentityTrackMatcher();
}

@@ -561,3 +565,3 @@ this._trackMatcher.update(sdp);

// NOTE(mmalavalli): In Firefox, if the remote RTCPeerConnection sends
// NOTE(mmalavalli): For "unified-plan" sdps, if the remote RTCPeerConnection sends
// an offer with fewer audio m= lines than the number of audio RTCRTPSenders

@@ -575,3 +579,3 @@ // in the local RTCPeerConnection, then the local RTCPeerConnection creates

// value > 1.
if (isFirefox) {
if (this._isUnifiedPlan) {
var senders = this._peerConnection.getSenders().filter(function (sender) {

@@ -649,3 +653,2 @@ return sender.track;

var revision = description.revision;
var vp8SimulcastRequested = this._preferredVideoCodecs.some(function (codecSettings) {

@@ -662,9 +665,6 @@ return codecSettings.codec.toLowerCase() === 'vp8' && codecSettings.simulcast;

type: description.type,
sdp: isChrome && vp8SimulcastRequested ? _this8._setSimulcast(description.sdp, _this8._trackIdsToAttributes) : description.sdp
sdp: isChrome && vp8SimulcastRequested ? _this8._setSimulcast(description.sdp, _this8._sdpFormat, _this8._trackIdsToAttributes) : description.sdp
};
}
description = new _this8._RTCSessionDescription(description);
if (description.type === 'answer') {
_this8._lastStableDescriptionRevision = revision;
}
return _this8._peerConnection.setLocalDescription(description);

@@ -683,2 +683,4 @@ }).catch(function (error) {

_this8._descriptionRevision++;
} else if (description.type === 'answer') {
_this8._lastStableDescriptionRevision = _this8._descriptionRevision;
}

@@ -720,3 +722,3 @@ _this8._localUfrag = getUfrag(description);

}
if (_this9._sdpFormat === 'unified' && _this9._peerConnection.getTransceivers) {
if (_this9._isUnifiedPlan && _this9._peerConnection.getTransceivers) {
_this9._peerConnection.getTransceivers().forEach(function (transceiver) {

@@ -789,5 +791,2 @@ if (shouldStopTransceiver(description, transceiver)) {

return Promise.resolve().then(function () {
if (description.type === 'answer') {
_this10._lastStableDescriptionRevision = revision;
}
return _this10._setRemoteDescription(description);

@@ -798,2 +797,3 @@ }).catch(function () {

if (description.type === 'answer') {
_this10._lastStableDescriptionRevision = revision;
_this10._needsInitialAnswer = false;

@@ -800,0 +800,0 @@ }

@@ -120,6 +120,23 @@ 'use strict';

_createClass(PeerConnectionManager, [{
key: '_getConfiguration',
key: '_closeAbsentPeerConnections',
/**
* Close the {@link PeerConnectionV2}s which are no longer relevant.
* @param {Array<object>} peerConnectionStates
* @returns {this}
*/
value: function _closeAbsentPeerConnections(peerConnectionStates) {
var peerConnectionIds = new Set(peerConnectionStates.map(function (peerConnectionState) {
return peerConnectionState.id;
}));
this._peerConnections.forEach(function (peerConnection) {
if (!peerConnectionIds.has(peerConnection.id)) {
peerConnection._close();
}
});
return this;
}
/**
* Get the {@link PeerConnectionManager}'s configuration.

@@ -129,2 +146,5 @@ * @private

*/
}, {
key: '_getConfiguration',
value: function _getConfiguration() {

@@ -312,2 +332,3 @@ return this._configurationDeferred.promise;

* @param {Array<object>} peerConnectionStates
* @param {boolean} [synced=false]
* @returns {Promise<this>}

@@ -321,2 +342,7 @@ */

var synced = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
if (synced) {
this._closeAbsentPeerConnections(peerConnectionStates);
}
return this._getConfiguration().then(function (configuration) {

@@ -323,0 +349,0 @@ return Promise.all(peerConnectionStates.map(function (peerConnectionState) {

@@ -276,4 +276,2 @@ 'use strict';

var participantsToKeep = new Set();
if (roomState.subscribed && roomState.subscribed.revision > this._subscribedRevision) {

@@ -283,8 +281,8 @@ this._subscribedRevision = roomState.subscribed.revision;

if (trackState.id) {
this._subscriptionFailures.delete(trackState.sid);
this._subscribed.set(trackState.id, trackState.sid);
} else if (trackState.error && !this._subscriptionFailures.has(trackState.sid)) {
this._subscriptionFailures.set(trackState.sid, trackState.error);
_this3._subscriptionFailures.delete(trackState.sid);
_this3._subscribed.set(trackState.id, trackState.sid);
} else if (trackState.error && !_this3._subscriptionFailures.has(trackState.sid)) {
_this3._subscriptionFailures.set(trackState.sid, trackState.error);
}
}, this);
});

@@ -304,12 +302,22 @@ var subscribedTrackIds = new Set(roomState.subscribed.tracks.filter(function (trackState) {

var participantsToKeep = new Set();
// TODO(mroberts): Remove me once the Server is fixed.
(roomState.participants || []).forEach(function (participantState) {
if (participantState.sid === this.localParticipant.sid || this._disconnectedParticipantSids.has(participantState.sid)) {
if (participantState.sid === _this3.localParticipant.sid || _this3._disconnectedParticipantSids.has(participantState.sid)) {
return;
}
var participant = this._getOrCreateRemoteParticipant(participantState);
var participant = _this3._getOrCreateRemoteParticipant(participantState);
participant.update(participantState);
participantsToKeep.add(participant);
}, this);
});
if (roomState.type === 'synced') {
this.participants.forEach(function (participant) {
if (!participantsToKeep.has(participant)) {
participant.disconnect();
}
});
}
handleSubscriptions(this);

@@ -320,3 +328,3 @@

if (roomState.peer_connections) {
this._peerConnectionManager.update(roomState.peer_connections);
this._peerConnectionManager.update(roomState.peer_connections, roomState.type === 'synced');
}

@@ -332,5 +340,5 @@

if (track.sid) {
this._published.set(track.id, track.sid);
_this3._published.set(track.id, track.sid);
}
}, this);
});
this.localParticipant.update(roomState.published);

@@ -337,0 +345,0 @@ }

@@ -18,14 +18,20 @@ 'use strict';

var StateMachine = require('../../statemachine');
var util = require('../../util');
var _require = require('../../util/twilio-video-errors'),
RoomCompletedError = _require.RoomCompletedError;
var _require = require('../../util'),
createMediaSignalingPayload = _require.createMediaSignalingPayload,
getSdpFormat = _require.getSdpFormat,
getUserAgent = _require.getUserAgent,
makeServerSIPURI = _require.makeServerSIPURI,
withJitter = _require.withJitter;
var _require2 = require('../../util/twilio-video-errors'),
SignalingConnectionDisconnectedError = _require2.SignalingConnectionDisconnectedError,
SignalingConnectionError = _require2.SignalingConnectionError,
SignalingConnectionTimeoutError = _require2.SignalingConnectionTimeoutError,
SignalingIncomingMessageInvalidError = _require2.SignalingIncomingMessageInvalidError,
createTwilioError = _require2.createTwilioError;
RoomCompletedError = _require2.RoomCompletedError;
var _require3 = require('../../util/twilio-video-errors'),
SignalingConnectionDisconnectedError = _require3.SignalingConnectionDisconnectedError,
SignalingConnectionError = _require3.SignalingConnectionError,
SignalingConnectionTimeoutError = _require3.SignalingConnectionTimeoutError,
SignalingIncomingMessageInvalidError = _require3.SignalingIncomingMessageInvalidError,
createTwilioError = _require3.createTwilioError;
var SDK_NAME = packageInfo.name + '.js';

@@ -95,3 +101,3 @@ var SDK_VERSION = packageInfo.version;

SIPJSMediaHandler: DefaultSIPJSMediaHandler,
userAgent: util.getUserAgent()
userAgent: getUserAgent()
}, options);

@@ -234,3 +240,3 @@

function createSession(transport, name, accessToken, localParticipant, peerConnectionManager, ua, SIPJSMediaHandler, iceServerSourceStatus, dominantSpeaker, networkQuality) {
var target = 'sip:' + util.makeServerSIPURI();
var target = 'sip:' + makeServerSIPURI();
return ua.invite(target, {

@@ -266,6 +272,6 @@ extraHeaders: [constants.headers.X_TWILIO_ACCESSTOKEN + ': ' + accessToken, 'Session-Expires: 120'],

};
message.media_signaling = util.createMediaSignalingPayload(dominantSpeaker, networkQuality);
message.media_signaling = createMediaSignalingPayload(dominantSpeaker, networkQuality);
}
var sdpFormat = util.getSdpFormat(transport._sdpSemantics);
var sdpFormat = getSdpFormat(transport._sdpSemantics);
if (type === 'connect' && sdpFormat) {

@@ -285,14 +291,2 @@ message.format = sdpFormat;

/**
* Add random jitter to a given value in the range [-jitter, jitter].
* @private
* @param {number} value
* @param {number} jitter
* @returns {number} value + random(-jitter, +jitter)
*/
function withJitter(value, jitter) {
var rand = Math.random();
return value - jitter + Math.floor(2 * jitter * rand + 0.5);
}
function publishWithRetries(transport, session, payload, attempts) {

@@ -299,0 +293,0 @@ attempts = attempts || 0;

@@ -16,9 +16,17 @@ 'use strict';

var TwilioConnection = require('../../twilioconnection');
var util = require('../../util');
var _require = require('../../util/twilio-video-errors'),
createTwilioError = _require.createTwilioError,
RoomCompletedError = _require.RoomCompletedError,
SignalingConnectionError = _require.SignalingConnectionError;
var _require = require('../../util'),
createMediaSignalingPayload = _require.createMediaSignalingPayload,
getSdpFormat = _require.getSdpFormat,
getUserAgent = _require.getUserAgent,
withJitter = _require.withJitter;
var _require2 = require('../../util/twilio-video-errors'),
createTwilioError = _require2.createTwilioError,
RoomCompletedError = _require2.RoomCompletedError,
SignalingConnectionError = _require2.SignalingConnectionError;
var MAX_RECONNECT_ATTEMPTS = 5;
var RECONNECT_BACKOFF_JITTER = 10;
var RECONNECT_BACKOFF_MS = 50;
var SDK_NAME = packageInfo.name + '.js';

@@ -86,3 +94,6 @@ var SDK_VERSION = packageInfo.version;

TwilioConnection: TwilioConnection,
userAgent: util.getUserAgent()
maxReconnectAttempts: MAX_RECONNECT_ATTEMPTS,
reconnectBackOffJitter: RECONNECT_BACKOFF_JITTER,
reconnectBackOffMs: RECONNECT_BACKOFF_MS,
userAgent: getUserAgent()
}, options);

@@ -120,5 +131,18 @@

},
_options: {
value: options
},
_peerConnectionManager: {
value: peerConnectionManager
},
_reconnectAttemptsLeft: {
value: options.maxReconnectAttempts,
writable: true
},
_reconnectBackOffJitter: {
value: options.reconnectBackOffJitter
},
_reconnectBackOffMs: {
value: options.reconnectBackOffMs
},
_sdpSemantics: {

@@ -132,3 +156,4 @@ value: options.sdpSemantics

_twilioConnection: {
value: new options.TwilioConnection(wsServer, options)
value: null,
writable: true
},

@@ -143,2 +168,5 @@ _updatesReceived: {

value: options.userAgent
},
_wsServer: {
value: wsServer
}

@@ -194,5 +222,5 @@ });

message.media_signaling = util.createMediaSignalingPayload(this._dominantSpeaker, this._networkQuality);
message.media_signaling = createMediaSignalingPayload(this._dominantSpeaker, this._networkQuality);
var sdpFormat = util.getSdpFormat(this._sdpSemantics);
var sdpFormat = getSdpFormat(this._sdpSemantics);
if (sdpFormat) {

@@ -359,3 +387,18 @@ message.format = sdpFormat;

function createOrResetTwilioConnection() {
if (transport._twilioConnection) {
transport._twilioConnection.removeListener('message', handleMessage);
}
var _options = transport._options,
_wsServer = transport._wsServer;
var TwilioConnection = transport._options.TwilioConnection;
transport._twilioConnection = new TwilioConnection(_wsServer, _options);
return transport._twilioConnection;
}
function disconnect(error) {
if (transport.state === 'disconnected') {
return;
}
if (!error) {

@@ -365,5 +408,37 @@ transport.disconnect();

}
transport.disconnect(new SignalingConnectionError());
if (transport._reconnectAttemptsLeft <= 0) {
transport.disconnect(new SignalingConnectionError());
return;
}
reconnect();
}
function reconnect() {
if (transport.state === 'connected') {
transport.preempt('syncing');
}
transport._reconnectAttemptsLeft--;
var maxReconnectAttempts = transport._options.maxReconnectAttempts;
var reconnectAttempts = maxReconnectAttempts - transport._reconnectAttemptsLeft;
var backOffMs = (1 << reconnectAttempts) * transport._reconnectBackOffMs;
setTimeout(startConnect, withJitter(backOffMs, transport._reconnectBackOffJitter));
}
function resetReconnectAttemptsLeft() {
var maxReconnectAttempts = transport._options.maxReconnectAttempts;
transport._reconnectAttemptsLeft = maxReconnectAttempts;
}
function startConnect() {
if (transport.state === 'disconnected') {
return;
}
var twilioConnection = createOrResetTwilioConnection();
twilioConnection.once('close', disconnect);
twilioConnection.on('message', handleMessage);
twilioConnection.once('open', connect);
}
function handleMessage(message) {

@@ -411,2 +486,5 @@ switch (transport.state) {

switch (message.type) {
case 'error':
transport.disconnect(createTwilioError(message.code, message.message));
return;
case 'connected':

@@ -417,2 +495,3 @@ case 'update':

case 'synced':
resetReconnectAttemptsLeft();
transport.emit('message', message);

@@ -434,7 +513,2 @@ transport.preempt('connected');

var twilioConnection = transport._twilioConnection;
twilioConnection.once('close', disconnect);
twilioConnection.on('message', handleMessage);
twilioConnection.once('open', connect);
transport.on('stateChanged', function stateChanged(state) {

@@ -454,3 +528,3 @@ switch (state) {

case 'disconnected':
twilioConnection.removeListener('message', handleMessage);
transport._twilioConnection.removeListener('message', handleMessage);
transport.removeListener('stateChanged', stateChanged);

@@ -466,4 +540,6 @@ return;

});
startConnect();
}
module.exports = TwilioConnectionTransport;

@@ -14,6 +14,10 @@ 'use strict';

var _require = require('./util'),
buildLogLevels = _require.buildLogLevels,
makeUUID = _require.makeUUID;
var Log = require('./util/log');
var Timeout = require('./util/timeout');
var nInstances = 0;
/*

@@ -39,3 +43,3 @@ TwilioConnection states

var DEFAULT_MAX_CONSECUTIVE_MISSED_HEARTBEATS = 5;
var DEFAULT_MAX_CONSECUTIVE_MISSED_HEARTBEATS = 3;
var DEFAULT_MAX_REQUESTED_HEARTBEAT_TIMEOUT = 5000;

@@ -74,2 +78,13 @@ var DEFAULT_WELCOME_TIMEOUT = 5000;

options = Object.assign({
maxConsecutiveMissedHeartbeats: DEFAULT_MAX_CONSECUTIVE_MISSED_HEARTBEATS,
requestedHeartbeatTimeout: DEFAULT_MAX_REQUESTED_HEARTBEAT_TIMEOUT,
welcomeTimeout: DEFAULT_WELCOME_TIMEOUT,
Log: Log,
WebSocket: WebSocket
}, options);
var logLevels = buildLogLevels(options.logLevel);
var log = new options.Log('default', _this, logLevels);
Object.defineProperties(_this, {

@@ -84,2 +99,8 @@ _consecutiveHeartbeatsMissed: {

},
_instanceId: {
value: ++nInstances
},
_log: {
value: log
},
_messageQueue: {

@@ -89,8 +110,3 @@ value: []

_options: {
value: Object.assign({
maxConsecutiveMissedHeartbeats: DEFAULT_MAX_CONSECUTIVE_MISSED_HEARTBEATS,
requestedHeartbeatTimeout: DEFAULT_MAX_REQUESTED_HEARTBEAT_TIMEOUT,
welcomeTimeout: DEFAULT_WELCOME_TIMEOUT,
WebSocket: WebSocket
}, options)
value: options
},

@@ -126,13 +142,58 @@ _sendHeartbeatTimeout: {

/**
* The number of consecutive "hearbeat" messages missed.
* @property {number}
*/
_createClass(TwilioConnection, [{
key: 'toString',
value: function toString() {
return '[TwilioConnection #' + this._instanceId + ': ' + this._ws.url + ']';
}
/**
* The number of consecutive "hearbeat" messages missed.
* @property {number}
*/
_createClass(TwilioConnection, [{
key: '_connect',
}, {
key: '_close',
/**
* Close the {@link TwilioConnection}.
* @param {{code: number, reason: string}} event
* @private
*/
value: function _close(_ref) {
var code = _ref.code,
reason = _ref.reason;
if (this.state === 'closed') {
return;
}
if (this._welcomeTimeout) {
this._welcomeTimeout.clear();
}
if (this._heartbeatTimeout) {
this._heartbeatTimeout.clear();
}
if (this._sendHeartbeatTimeout) {
this._sendHeartbeatTimeout.clear();
}
this._messageQueue.splice(0);
var log = this._log;
if (code === WS_CLOSE_NORMAL) {
log.debug('Closed');
} else {
log.warn('Closed: ' + code + ' - ' + reason);
}
this.transition('closed', null, code !== WS_CLOSE_NORMAL ? new Error('WebSocket Error ' + code + ': ' + reason) : null);
var readyState = this._ws.readyState;
var WebSocket = this._options.WebSocket;
if (readyState !== WebSocket.CLOSING && readyState !== WebSocket.CLOSED) {
this._ws.close(code, reason);
}
}
/**
* Connect to the TCMP server.

@@ -142,2 +203,5 @@ * @param {string} serverUrl

*/
}, {
key: '_connect',
value: function _connect(serverUrl) {

@@ -147,19 +211,12 @@ var _this2 = this;

this._ws = new this._options.WebSocket(serverUrl);
var log = this._log;
var ws = this._ws;
log.debug('Created a new WebSocket:', ws);
ws.addEventListener('close', function (event) {
if (_this2._welcomeTimeout) {
_this2._welcomeTimeout.clear();
}
if (_this2._heartbeatTimeout) {
_this2._heartbeatTimeout.clear();
}
if (_this2._sendHeartbeatTimeout) {
_this2._sendHeartbeatTimeout.clear();
}
_this2._messageQueue.splice(0);
_this2.transition('closed', null, event.code !== WS_CLOSE_NORMAL ? new Error('WebSocket Error ' + event.code + ': ' + event.reason) : null);
return _this2._close(event);
});
ws.addEventListener('message', function (message) {
log.debug('Incoming: ' + message.data);
try {

@@ -188,2 +245,3 @@ message = JSON.parse(message.data);

default:
_this2._log.debug('Unknown message type: ' + message.type);
_this2.emit('error', new Error('Unknown message type: ' + message.type));

@@ -195,2 +253,3 @@ break;

ws.addEventListener('open', function () {
log.debug('WebSocket opened:', ws);
_this2._sendHello();

@@ -213,9 +272,12 @@ var welcomeTimeout = _this2._options.welcomeTimeout;

key: '_handleBad',
value: function _handleBad(_ref) {
var reason = _ref.reason;
value: function _handleBad(_ref2) {
var reason = _ref2.reason;
var log = this._log;
if (this.state === 'connecting') {
this._ws.close(WS_CLOSE_HELLO_FAILED, reason);
log.warn('Closing: ' + WS_CLOSE_HELLO_FAILED + ' - ' + reason);
this._close({ code: WS_CLOSE_HELLO_FAILED, reason: reason });
return;
}
log.debug('Error: ' + reason);
this.emit('error', new Error(reason));

@@ -251,5 +313,7 @@ }

this._consecutiveHeartbeatsMissed++;
var log = this._log;
var maxConsecutiveMissedHeartbeats = this._options.maxConsecutiveMissedHeartbeats;
log.debug('Consecutive heartbeats missed: ' + this._consecutiveHeartbeatsMissed);
if (this._consecutiveHeartbeatsMissed < maxConsecutiveMissedHeartbeats) {

@@ -259,3 +323,6 @@ this._heartbeatTimeout.reset();

}
this._ws.close(WS_CLOSE_HEARTBEATS_MISSED, 'Missed ' + maxConsecutiveMissedHeartbeats + ' "heartbeat" messages');
var reason = 'Missed ' + maxConsecutiveMissedHeartbeats + ' "heartbeat" messages';
log.warn('Closing: ' + WS_CLOSE_HEARTBEATS_MISSED + ' - ' + reason);
this._close({ code: WS_CLOSE_HEARTBEATS_MISSED, reason: reason });
}

@@ -271,4 +338,4 @@

key: '_handleMessage',
value: function _handleMessage(_ref2) {
var body = _ref2.body;
value: function _handleMessage(_ref3) {
var body = _ref3.body;

@@ -289,6 +356,6 @@ if (this.state !== 'open') {

key: '_handleWelcome',
value: function _handleWelcome(_ref3) {
value: function _handleWelcome(_ref4) {
var _this3 = this;
var negotiatedTimeout = _ref3.negotiatedTimeout;
var negotiatedTimeout = _ref4.negotiatedTimeout;

@@ -323,3 +390,5 @@ if (this.state !== 'connecting') {

}
this._ws.close(WS_CLOSE_WELCOME_TIMEOUT, '"welcome" message timeout expired');
var reason = '"welcome" message timeout expired';
this._log.warn('Closing: ' + WS_CLOSE_WELCOME_TIMEOUT + ' - ' + reason);
this._close({ code: WS_CLOSE_WELCOME_TIMEOUT, reason: reason });
}

@@ -336,3 +405,10 @@

value: function _send(message) {
this._ws.send(JSON.stringify(message));
var readyState = this._ws.readyState;
var WebSocket = this._options.WebSocket;
if (readyState === WebSocket.OPEN) {
var data = JSON.stringify(message);
this._log.debug('Outgoing: ' + data);
this._ws.send(data);
}
}

@@ -407,3 +483,3 @@

this._sendOrEnqueue({ type: 'bye' });
this._ws.close();
this._ws.close(WS_CLOSE_NORMAL);
}

@@ -458,2 +534,3 @@

* @typedef {object} TwilioConnectionOptions
* @property {LogLevel} [logLevel=warn] - Log level of the {@link TwilioConnection}
* @property {number} [maxConsecutiveMissedHeartbeats=5] - Max. number of consecutive "heartbeat" messages that can be missed

@@ -460,0 +537,0 @@ * @property {number} [requestedHeartbeatTimeout=5000] - "heartbeat" timeout (ms) requested by the {@link TwilioConnection}

@@ -642,2 +642,14 @@ /* globals mozRTCPeerConnection */

/**
* Add random jitter to a given value in the range [-jitter, jitter].
* @private
* @param {number} value
* @param {number} jitter
* @returns {number} value + random(-jitter, +jitter)
*/
function withJitter(value, jitter) {
var rand = Math.random();
return value - jitter + Math.floor(2 * jitter * rand + 0.5);
}
exports.constants = constants;

@@ -671,2 +683,3 @@ exports.createMediaSignalingPayload = createMediaSignalingPayload;

exports.getSdpFormat = getSdpFormat;
exports.valueToJSON = valueToJSON;
exports.valueToJSON = valueToJSON;
exports.withJitter = withJitter;

@@ -233,6 +233,7 @@ 'use strict';

* @param {string} sdp
* @param {'planb' | 'unified'} sdpFormat
* @param {Map<Track.ID, TrackAttributes>} trackIdsToAttributes
* @returns {string} Updated SDP string
*/
function setSimulcast(sdp, trackIdsToAttributes) {
function setSimulcast(sdp, sdpFormat, trackIdsToAttributes) {
var mediaSections = getMediaSections(sdp);

@@ -252,3 +253,3 @@ var session = sdp.split('\r\nm=')[0];

});
return hasVP8PayloadType ? setSimulcastInMediaSection(section, trackIdsToAttributes) : section;
return hasVP8PayloadType ? setSimulcastInMediaSection(section, sdpFormat, trackIdsToAttributes) : section;
})).concat('').join('\r\n');

@@ -255,0 +256,0 @@ }

'use strict';
var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }();
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();

@@ -32,3 +34,3 @@

* @param {Track.ID} trackId - The MediaStreamTrack ID
* @param {string} streamId - The MediaStream ID
* @param {MediaStreamID} streamId - The MediaStream ID
* @param {string} cName - The MediaStream cname

@@ -199,10 +201,50 @@ */

/**
* Create SSRC attribute tuples.
* @param {string} section
* @param {'planb' | 'unified'} sdpFormat
* @returns {Array<[SSRC, MediaStreamID, Track.ID]>}
*/
function createSSRCAttributeTuples(section, sdpFormat) {
return {
planb: createPlanBSSRCAttributeTuples,
unified: createUnifiedPlanSSRCAttributeTuples
}[sdpFormat](section);
}
/**
* Create "plan-b" SSRC attribute tuples.
* @param {string} section
* @returns {Array<[SSRC, MediaStreamID, Track.ID]>}
*/
function createPlanBSSRCAttributeTuples(section) {
return getMatches(section, '^a=ssrc:([0-9]+) msid:([^\\s]+) ([^\\s]+)$');
}
/**
* Create "unified-plan" SSRC attribute tuples.
* @param {string} section
* @returns {Array<[SSRC, MediaStreamID, Track.ID]>}
*/
function createUnifiedPlanSSRCAttributeTuples(section) {
var _flatMap = flatMap(getMatches(section, '^a=msid:(.+) (.+)$')),
_flatMap2 = _slicedToArray(_flatMap, 2),
streamId = _flatMap2[0],
trackId = _flatMap2[1];
var ssrcs = flatMap(getMatches(section, '^a=ssrc:(.+) cname:.+$'));
return ssrcs.map(function (ssrc) {
return [ssrc, streamId, trackId];
});
}
/**
* Create a Map of MediaStreamTrack IDs and their {@link TrackAttributes}.
* @param {string} section - SDP media section
* @param {'planb' | 'unified'} sdpFormat
* @returns {Map<Track.ID, TrackAttributes>}
*/
function createTrackIdsToAttributes(section) {
function createTrackIdsToAttributes(section, sdpFormat) {
var simSSRCs = getSimulcastSSRCs(section);
var ssrcAttrTuples = getMatches(section, '^a=ssrc:([0-9]+) msid:([^\\s]+) ([^\\s]+)$');
var rtxPairs = getSSRCRtxPairs(section);
var ssrcAttrTuples = createSSRCAttributeTuples(section, sdpFormat);

@@ -225,2 +267,3 @@ return ssrcAttrTuples.reduce(function (trackIdsToSSRCs, tuple) {

* @param {string} section - SDP media section
* @param {'planb' | 'unified'} sdpFormat
* @param {Map<Track.ID, TrackAttributes>} trackIdsToAttributes - Existing

@@ -230,4 +273,4 @@ * map which will be updated for new MediaStreamTrack IDs

*/
function setSimulcastInMediaSection(section, trackIdsToAttributes) {
var newTrackIdsToAttributes = createTrackIdsToAttributes(section);
function setSimulcastInMediaSection(section, sdpFormat, trackIdsToAttributes) {
var newTrackIdsToAttributes = createTrackIdsToAttributes(section, sdpFormat);
var newTrackIds = Array.from(newTrackIdsToAttributes.keys());

@@ -263,7 +306,18 @@ var trackIds = Array.from(trackIdsToAttributes.keys());

// "relevantSdpLines" are removed.
var sectionLines = new Set(section.split('\r\n').concat(relevantSdpLines));
return flatMap(sectionLines).join('\r\n');
var sectionLines = flatMap(new Set(section.split('\r\n').concat(relevantSdpLines)));
var xGoogleFlagConference = 'a=x-google-flag:conference';
if (!section.match(xGoogleFlagConference)) {
sectionLines.push(xGoogleFlagConference);
}
return sectionLines.join('\r\n');
}
/**
* String representing a MediaStream ID.
* @typedef {string} MediaStreamID
*/
/**
* String representing the SSRC of a MediaStreamTrack.

@@ -270,0 +324,0 @@ * @typedef {string} SSRC

@@ -391,5 +391,7 @@ 'use strict';

log.debug('Creating a new Room:', room);
roomSignaling.on('stateChanged', function stateChanged() {
log.info('Disconnected from Room:', room.toString());
roomSignaling.removeListener('stateChanged', stateChanged);
roomSignaling.on('stateChanged', function stateChanged(state) {
if (state === 'disconnected') {
log.info('Disconnected from Room:', room.toString());
roomSignaling.removeListener('stateChanged', stateChanged);
}
});

@@ -396,0 +398,0 @@

@@ -162,4 +162,6 @@ 'use strict';

const trackSignaling = signaling.getPublication(localTrack._trackSender);
trackSignaling.disable();
log.debug(`Disabled the ${util.trackClass(localTrack, true)}:`, localTrack.id);
if (trackSignaling) {
trackSignaling.disable();
log.debug(`Disabled the ${util.trackClass(localTrack, true)}:`, localTrack.id);
}
}

@@ -169,4 +171,6 @@

const trackSignaling = signaling.getPublication(localTrack._trackSender);
trackSignaling.enable();
log.debug(`Enabled the ${util.trackClass(localTrack, true)}:`, localTrack.id);
if (trackSignaling) {
trackSignaling.enable();
log.debug(`Enabled the ${util.trackClass(localTrack, true)}:`, localTrack.id);
}
}

@@ -178,3 +182,5 @@

const trackSignaling = signaling.getPublication(localTrack._trackSender);
trackSignaling.stop();
if (trackSignaling) {
trackSignaling.stop();
}
}

@@ -181,0 +187,0 @@

@@ -62,2 +62,3 @@ 'use strict';

environment: options.environment,
logLevel: options.logLevel,
networkQuality: options.networkQuality,

@@ -64,0 +65,0 @@ iceServerSourceStatus: iceServerSource.status,

@@ -103,4 +103,5 @@ 'use strict';

const sdpFormat = getSdpFormat(options.sdpSemantics);
const isUnifiedPlan = sdpFormat === 'unified';
const localMediaStream = isFirefox && RTCPeerConnection.prototype.addTransceiver
const localMediaStream = isUnifiedPlan && RTCPeerConnection.prototype.addTransceiver
? null

@@ -110,3 +111,3 @@ : new options.MediaStream();

if (options.dummyAudioMediaStreamTrack) {
peerConnection.addTrack(options.dummyAudioMediaStreamTrack, localMediaStream || new MediaStream());
peerConnection.addTrack(options.dummyAudioMediaStreamTrack, localMediaStream || new options.MediaStream());
}

@@ -140,2 +141,5 @@

},
_isUnifiedPlan: {
value: isUnifiedPlan
},
_lastIceConnectionState: {

@@ -472,3 +476,3 @@ writable: true,

// the same technique as Firefox.
: isSafari || this._sdpFormat === 'unified'
: isSafari || this._isUnifiedPlan
? new OrderedTrackMatcher()

@@ -500,3 +504,3 @@ : new IdentityTrackMatcher();

// NOTE(mmalavalli): In Firefox, if the remote RTCPeerConnection sends
// NOTE(mmalavalli): For "unified-plan" sdps, if the remote RTCPeerConnection sends
// an offer with fewer audio m= lines than the number of audio RTCRTPSenders

@@ -514,3 +518,3 @@ // in the local RTCPeerConnection, then the local RTCPeerConnection creates

// value > 1.
if (isFirefox) {
if (this._isUnifiedPlan) {
const senders = this._peerConnection.getSenders().filter(sender => sender.track);

@@ -577,3 +581,2 @@ shouldReoffer = ['audio', 'video'].reduce((shouldOffer, kind) => {

_setLocalDescription(description) {
const revision = description.revision;
const vp8SimulcastRequested = this._preferredVideoCodecs.some(codecSettings => codecSettings.codec.toLowerCase() === 'vp8' && codecSettings.simulcast);

@@ -589,3 +592,3 @@

sdp: isChrome && vp8SimulcastRequested
? this._setSimulcast(description.sdp, this._trackIdsToAttributes)
? this._setSimulcast(description.sdp, this._sdpFormat, this._trackIdsToAttributes)
: description.sdp

@@ -595,5 +598,2 @@ };

description = new this._RTCSessionDescription(description);
if (description.type === 'answer') {
this._lastStableDescriptionRevision = revision;
}
return this._peerConnection.setLocalDescription(description);

@@ -612,2 +612,4 @@ }).catch(error => {

this._descriptionRevision++;
} else if (description.type === 'answer') {
this._lastStableDescriptionRevision = this._descriptionRevision;
}

@@ -651,3 +653,3 @@ this._localUfrag = getUfrag(description);

}
if (this._sdpFormat === 'unified' && this._peerConnection.getTransceivers) {
if (this._isUnifiedPlan && this._peerConnection.getTransceivers) {
this._peerConnection.getTransceivers().forEach(transceiver => {

@@ -716,11 +718,7 @@ if (shouldStopTransceiver(description, transceiver)) {

const revision = description.revision;
return Promise.resolve().then(() => {
if (description.type === 'answer') {
this._lastStableDescriptionRevision = revision;
}
return this._setRemoteDescription(description);
}).catch(() => {
return Promise.resolve().then(() => this._setRemoteDescription(description)).catch(() => {
throw new MediaClientRemoteDescFailedError();
}).then(() => {
if (description.type === 'answer') {
this._lastStableDescriptionRevision = revision;
this._needsInitialAnswer = false;

@@ -727,0 +725,0 @@ }

@@ -112,2 +112,17 @@ 'use strict';

/**
* Close the {@link PeerConnectionV2}s which are no longer relevant.
* @param {Array<object>} peerConnectionStates
* @returns {this}
*/
_closeAbsentPeerConnections(peerConnectionStates) {
const peerConnectionIds = new Set(peerConnectionStates.map(peerConnectionState => peerConnectionState.id));
this._peerConnections.forEach(peerConnection => {
if (!peerConnectionIds.has(peerConnection.id)) {
peerConnection._close();
}
});
return this;
}
/**
* Get the {@link PeerConnectionManager}'s configuration.

@@ -269,5 +284,9 @@ * @private

* @param {Array<object>} peerConnectionStates
* @param {boolean} [synced=false]
* @returns {Promise<this>}
*/
update(peerConnectionStates) {
update(peerConnectionStates, synced = false) {
if (synced) {
this._closeAbsentPeerConnections(peerConnectionStates);
}
return this._getConfiguration().then(configuration => {

@@ -274,0 +293,0 @@ return Promise.all(peerConnectionStates.map(peerConnectionState => {

@@ -233,7 +233,5 @@ 'use strict';

_update(roomState) {
const participantsToKeep = new Set();
if (roomState.subscribed && roomState.subscribed.revision > this._subscribedRevision) {
this._subscribedRevision = roomState.subscribed.revision;
roomState.subscribed.tracks.forEach(function(trackState) {
roomState.subscribed.tracks.forEach(trackState => {
if (trackState.id) {

@@ -245,3 +243,3 @@ this._subscriptionFailures.delete(trackState.sid);

}
}, this);
});

@@ -259,4 +257,6 @@ const subscribedTrackIds = new Set(roomState.subscribed.tracks

const participantsToKeep = new Set();
// TODO(mroberts): Remove me once the Server is fixed.
(roomState.participants || []).forEach(function(participantState) {
(roomState.participants || []).forEach(participantState => {
if (participantState.sid === this.localParticipant.sid ||

@@ -269,4 +269,12 @@ this._disconnectedParticipantSids.has(participantState.sid)) {

participantsToKeep.add(participant);
}, this);
});
if (roomState.type === 'synced') {
this.participants.forEach(participant => {
if (!participantsToKeep.has(participant)) {
participant.disconnect();
}
});
}
handleSubscriptions(this);

@@ -277,3 +285,3 @@

if (roomState.peer_connections) {
this._peerConnectionManager.update(roomState.peer_connections);
this._peerConnectionManager.update(roomState.peer_connections, roomState.type === 'synced');
}

@@ -287,7 +295,7 @@

this._publishedRevision = roomState.published.revision;
roomState.published.tracks.forEach(function(track) {
roomState.published.tracks.forEach(track => {
if (track.sid) {
this._published.set(track.id, track.sid);
}
}, this);
});
this.localParticipant.update(roomState.published);

@@ -294,0 +302,0 @@ }

@@ -10,3 +10,10 @@ 'use strict';

const StateMachine = require('../../statemachine');
const util = require('../../util');
const {
createMediaSignalingPayload,
getSdpFormat,
getUserAgent,
makeServerSIPURI,
withJitter
} = require('../../util');
const { RoomCompletedError } = require('../../util/twilio-video-errors');

@@ -90,3 +97,3 @@

SIPJSMediaHandler: DefaultSIPJSMediaHandler,
userAgent: util.getUserAgent()
userAgent: getUserAgent()
}, options);

@@ -228,3 +235,3 @@ super('connecting', states);

function createSession(transport, name, accessToken, localParticipant, peerConnectionManager, ua, SIPJSMediaHandler, iceServerSourceStatus, dominantSpeaker, networkQuality) {
const target = `sip:${util.makeServerSIPURI()}`;
const target = `sip:${makeServerSIPURI()}`;
return ua.invite(target, {

@@ -263,3 +270,3 @@ extraHeaders: [

};
message.media_signaling = util.createMediaSignalingPayload(
message.media_signaling = createMediaSignalingPayload(
dominantSpeaker,

@@ -269,3 +276,3 @@ networkQuality);

const sdpFormat = util.getSdpFormat(transport._sdpSemantics);
const sdpFormat = getSdpFormat(transport._sdpSemantics);
if (type === 'connect' && sdpFormat) {

@@ -285,14 +292,2 @@ message.format = sdpFormat;

/**
* Add random jitter to a given value in the range [-jitter, jitter].
* @private
* @param {number} value
* @param {number} jitter
* @returns {number} value + random(-jitter, +jitter)
*/
function withJitter(value, jitter) {
const rand = Math.random();
return value - jitter + Math.floor(2 * jitter * rand + 0.5);
}
function publishWithRetries(transport, session, payload, attempts) {

@@ -299,0 +294,0 @@ attempts = attempts || 0;

@@ -8,5 +8,11 @@ 'use strict';

const TwilioConnection = require('../../twilioconnection');
const util = require('../../util');
const {
createMediaSignalingPayload,
getSdpFormat,
getUserAgent,
withJitter
} = require('../../util');
const {
createTwilioError,

@@ -17,2 +23,5 @@ RoomCompletedError,

const MAX_RECONNECT_ATTEMPTS = 5;
const RECONNECT_BACKOFF_JITTER = 10;
const RECONNECT_BACKOFF_MS = 50;
const SDK_NAME = `${packageInfo.name}.js`;

@@ -84,3 +93,6 @@ const SDK_VERSION = packageInfo.version;

TwilioConnection,
userAgent: util.getUserAgent()
maxReconnectAttempts: MAX_RECONNECT_ATTEMPTS,
reconnectBackOffJitter: RECONNECT_BACKOFF_JITTER,
reconnectBackOffMs: RECONNECT_BACKOFF_MS,
userAgent: getUserAgent()
}, options);

@@ -123,5 +135,18 @@ super('connecting', states);

},
_options: {
value: options
},
_peerConnectionManager: {
value: peerConnectionManager
},
_reconnectAttemptsLeft: {
value: options.maxReconnectAttempts,
writable: true
},
_reconnectBackOffJitter: {
value: options.reconnectBackOffJitter
},
_reconnectBackOffMs: {
value: options.reconnectBackOffMs
},
_sdpSemantics: {

@@ -135,3 +160,4 @@ value: options.sdpSemantics

_twilioConnection: {
value: new options.TwilioConnection(wsServer, options)
value: null,
writable: true
},

@@ -146,2 +172,5 @@ _updatesReceived: {

value: options.userAgent
},
_wsServer: {
value: wsServer
}

@@ -192,7 +221,7 @@ });

message.media_signaling = util.createMediaSignalingPayload(
message.media_signaling = createMediaSignalingPayload(
this._dominantSpeaker,
this._networkQuality);
const sdpFormat = util.getSdpFormat(this._sdpSemantics);
const sdpFormat = getSdpFormat(this._sdpSemantics);
if (sdpFormat) {

@@ -345,3 +374,16 @@ message.format = sdpFormat;

function createOrResetTwilioConnection() {
if (transport._twilioConnection) {
transport._twilioConnection.removeListener('message', handleMessage);
}
const { _options, _wsServer } = transport;
const { TwilioConnection } = transport._options;
transport._twilioConnection = new TwilioConnection(_wsServer, _options);
return transport._twilioConnection;
}
function disconnect(error) {
if (transport.state === 'disconnected') {
return;
}
if (!error) {

@@ -351,5 +393,35 @@ transport.disconnect();

}
transport.disconnect(new SignalingConnectionError());
if (transport._reconnectAttemptsLeft <= 0) {
transport.disconnect(new SignalingConnectionError());
return;
}
reconnect();
}
function reconnect() {
if (transport.state === 'connected') {
transport.preempt('syncing');
}
transport._reconnectAttemptsLeft--;
const { maxReconnectAttempts } = transport._options;
const reconnectAttempts = maxReconnectAttempts - transport._reconnectAttemptsLeft;
const backOffMs = (1 << reconnectAttempts) * transport._reconnectBackOffMs;
setTimeout(startConnect, withJitter(backOffMs, transport._reconnectBackOffJitter));
}
function resetReconnectAttemptsLeft() {
const { maxReconnectAttempts } = transport._options;
transport._reconnectAttemptsLeft = maxReconnectAttempts;
}
function startConnect() {
if (transport.state === 'disconnected') {
return;
}
const twilioConnection = createOrResetTwilioConnection();
twilioConnection.once('close', disconnect);
twilioConnection.on('message', handleMessage);
twilioConnection.once('open', connect);
}
function handleMessage(message) {

@@ -401,2 +473,5 @@ switch (transport.state) {

switch (message.type) {
case 'error':
transport.disconnect(createTwilioError(message.code, message.message));
return;
case 'connected':

@@ -407,2 +482,3 @@ case 'update':

case 'synced':
resetReconnectAttemptsLeft();
transport.emit('message', message);

@@ -426,7 +502,2 @@ transport.preempt('connected');

const twilioConnection = transport._twilioConnection;
twilioConnection.once('close', disconnect);
twilioConnection.on('message', handleMessage);
twilioConnection.once('open', connect);
transport.on('stateChanged', function stateChanged(state) {

@@ -443,3 +514,3 @@ switch (state) {

case 'disconnected':
twilioConnection.removeListener('message', handleMessage);
transport._twilioConnection.removeListener('message', handleMessage);
transport.removeListener('stateChanged', stateChanged);

@@ -455,4 +526,6 @@ return;

});
startConnect();
}
module.exports = TwilioConnectionTransport;
'use strict';
const StateMachine = require('./statemachine');
const { makeUUID } = require('./util');
const { buildLogLevels, makeUUID } = require('./util');
const Log = require('./util/log');
const Timeout = require('./util/timeout');
let nInstances = 0;
/*

@@ -27,3 +30,3 @@ TwilioConnection states

const DEFAULT_MAX_CONSECUTIVE_MISSED_HEARTBEATS = 5;
const DEFAULT_MAX_CONSECUTIVE_MISSED_HEARTBEATS = 3;
const DEFAULT_MAX_REQUESTED_HEARTBEAT_TIMEOUT = 5000;

@@ -57,2 +60,13 @@ const DEFAULT_WELCOME_TIMEOUT = 5000;

options = Object.assign({
maxConsecutiveMissedHeartbeats: DEFAULT_MAX_CONSECUTIVE_MISSED_HEARTBEATS,
requestedHeartbeatTimeout: DEFAULT_MAX_REQUESTED_HEARTBEAT_TIMEOUT,
welcomeTimeout: DEFAULT_WELCOME_TIMEOUT,
Log,
WebSocket
}, options);
const logLevels = buildLogLevels(options.logLevel);
const log = new options.Log('default', this, logLevels);
Object.defineProperties(this, {

@@ -67,2 +81,8 @@ _consecutiveHeartbeatsMissed: {

},
_instanceId: {
value: ++nInstances
},
_log: {
value: log
},
_messageQueue: {

@@ -72,8 +92,3 @@ value: []

_options: {
value: Object.assign({
maxConsecutiveMissedHeartbeats: DEFAULT_MAX_CONSECUTIVE_MISSED_HEARTBEATS,
requestedHeartbeatTimeout: DEFAULT_MAX_REQUESTED_HEARTBEAT_TIMEOUT,
welcomeTimeout: DEFAULT_WELCOME_TIMEOUT,
WebSocket
}, options)
value: options
},

@@ -102,2 +117,6 @@ _sendHeartbeatTimeout: {

toString() {
return `[TwilioConnection #${this._instanceId}: ${this._ws.url}]`;
}
/**

@@ -112,2 +131,40 @@ * The number of consecutive "hearbeat" messages missed.

/**
* Close the {@link TwilioConnection}.
* @param {{code: number, reason: string}} event
* @private
*/
_close({ code, reason }) {
if (this.state === 'closed') {
return;
}
if (this._welcomeTimeout) {
this._welcomeTimeout.clear();
}
if (this._heartbeatTimeout) {
this._heartbeatTimeout.clear();
}
if (this._sendHeartbeatTimeout) {
this._sendHeartbeatTimeout.clear();
}
this._messageQueue.splice(0);
const log = this._log;
if (code === WS_CLOSE_NORMAL) {
log.debug('Closed');
} else {
log.warn(`Closed: ${code} - ${reason}`);
}
this.transition('closed', null, code !== WS_CLOSE_NORMAL
? new Error(`WebSocket Error ${code}: ${reason}`)
: null);
const { readyState } = this._ws;
const { WebSocket } = this._options;
if (readyState !== WebSocket.CLOSING && readyState !== WebSocket.CLOSED) {
this._ws.close(code, reason);
}
}
/**
* Connect to the TCMP server.

@@ -119,21 +176,10 @@ * @param {string} serverUrl

this._ws = new this._options.WebSocket(serverUrl);
const log = this._log;
const ws = this._ws;
ws.addEventListener('close', event => {
if (this._welcomeTimeout) {
this._welcomeTimeout.clear();
}
if (this._heartbeatTimeout) {
this._heartbeatTimeout.clear();
}
if (this._sendHeartbeatTimeout) {
this._sendHeartbeatTimeout.clear();
}
this._messageQueue.splice(0);
this.transition('closed', null, event.code !== WS_CLOSE_NORMAL
? new Error(`WebSocket Error ${event.code}: ${event.reason}`)
: null);
});
log.debug('Created a new WebSocket:', ws);
ws.addEventListener('close', event => this._close(event));
ws.addEventListener('message', message => {
log.debug(`Incoming: ${message.data}`);
try {

@@ -162,2 +208,3 @@ message = JSON.parse(message.data);

default:
this._log.debug(`Unknown message type: ${message.type}`);
this.emit('error', new Error(`Unknown message type: ${message.type}`));

@@ -169,2 +216,3 @@ break;

ws.addEventListener('open', () => {
log.debug('WebSocket opened:', ws);
this._sendHello();

@@ -182,6 +230,9 @@ const { welcomeTimeout } = this._options;

_handleBad({ reason }) {
const log = this._log;
if (this.state === 'connecting') {
this._ws.close(WS_CLOSE_HELLO_FAILED, reason);
log.warn(`Closing: ${WS_CLOSE_HELLO_FAILED} - ${reason}`);
this._close({ code: WS_CLOSE_HELLO_FAILED, reason });
return;
}
log.debug(`Error: ${reason}`);
this.emit('error', new Error(reason));

@@ -211,4 +262,6 @@ }

this._consecutiveHeartbeatsMissed++;
const log = this._log;
const { maxConsecutiveMissedHeartbeats } = this._options;
log.debug(`Consecutive heartbeats missed: ${this._consecutiveHeartbeatsMissed}`);
if (this._consecutiveHeartbeatsMissed < maxConsecutiveMissedHeartbeats) {

@@ -218,4 +271,6 @@ this._heartbeatTimeout.reset();

}
this._ws.close(WS_CLOSE_HEARTBEATS_MISSED,
`Missed ${maxConsecutiveMissedHeartbeats} "heartbeat" messages`);
const reason = `Missed ${maxConsecutiveMissedHeartbeats} "heartbeat" messages`;
log.warn(`Closing: ${WS_CLOSE_HEARTBEATS_MISSED} - ${reason}`);
this._close({ code: WS_CLOSE_HEARTBEATS_MISSED, reason });
}

@@ -260,3 +315,5 @@

}
this._ws.close(WS_CLOSE_WELCOME_TIMEOUT, '"welcome" message timeout expired');
const reason = '"welcome" message timeout expired';
this._log.warn(`Closing: ${WS_CLOSE_WELCOME_TIMEOUT} - ${reason}`);
this._close({ code: WS_CLOSE_WELCOME_TIMEOUT, reason });
}

@@ -270,3 +327,9 @@

_send(message) {
this._ws.send(JSON.stringify(message));
const { readyState } = this._ws;
const { WebSocket } = this._options;
if (readyState === WebSocket.OPEN) {
const data = JSON.stringify(message);
this._log.debug(`Outgoing: ${data}`);
this._ws.send(data);
}
}

@@ -324,3 +387,3 @@

this._sendOrEnqueue({ type: 'bye' });
this._ws.close();
this._ws.close(WS_CLOSE_NORMAL);
}

@@ -364,2 +427,3 @@

* @typedef {object} TwilioConnectionOptions
* @property {LogLevel} [logLevel=warn] - Log level of the {@link TwilioConnection}
* @property {number} [maxConsecutiveMissedHeartbeats=5] - Max. number of consecutive "heartbeat" messages that can be missed

@@ -366,0 +430,0 @@ * @property {number} [requestedHeartbeatTimeout=5000] - "heartbeat" timeout (ms) requested by the {@link TwilioConnection}

@@ -618,2 +618,14 @@ /* globals mozRTCPeerConnection */

/**
* Add random jitter to a given value in the range [-jitter, jitter].
* @private
* @param {number} value
* @param {number} jitter
* @returns {number} value + random(-jitter, +jitter)
*/
function withJitter(value, jitter) {
const rand = Math.random();
return value - jitter + Math.floor(2 * jitter * rand + 0.5);
}
exports.constants = constants;

@@ -648,1 +660,2 @@ exports.createMediaSignalingPayload = createMediaSignalingPayload;

exports.valueToJSON = valueToJSON;
exports.withJitter = withJitter;

@@ -231,6 +231,7 @@ 'use strict';

* @param {string} sdp
* @param {'planb' | 'unified'} sdpFormat
* @param {Map<Track.ID, TrackAttributes>} trackIdsToAttributes
* @returns {string} Updated SDP string
*/
function setSimulcast(sdp, trackIdsToAttributes) {
function setSimulcast(sdp, sdpFormat, trackIdsToAttributes) {
const mediaSections = getMediaSections(sdp);

@@ -249,3 +250,3 @@ const session = sdp.split('\r\nm=')[0];

return hasVP8PayloadType
? setSimulcastInMediaSection(section, trackIdsToAttributes)
? setSimulcastInMediaSection(section, sdpFormat, trackIdsToAttributes)
: section;

@@ -252,0 +253,0 @@ })).concat('').join('\r\n');

@@ -27,3 +27,3 @@ 'use strict';

* @param {Track.ID} trackId - The MediaStreamTrack ID
* @param {string} streamId - The MediaStream ID
* @param {MediaStreamID} streamId - The MediaStream ID
* @param {string} cName - The MediaStream cname

@@ -174,10 +174,44 @@ */

/**
* Create SSRC attribute tuples.
* @param {string} section
* @param {'planb' | 'unified'} sdpFormat
* @returns {Array<[SSRC, MediaStreamID, Track.ID]>}
*/
function createSSRCAttributeTuples(section, sdpFormat) {
return {
planb: createPlanBSSRCAttributeTuples,
unified: createUnifiedPlanSSRCAttributeTuples
}[sdpFormat](section);
}
/**
* Create "plan-b" SSRC attribute tuples.
* @param {string} section
* @returns {Array<[SSRC, MediaStreamID, Track.ID]>}
*/
function createPlanBSSRCAttributeTuples(section) {
return getMatches(section, '^a=ssrc:([0-9]+) msid:([^\\s]+) ([^\\s]+)$');
}
/**
* Create "unified-plan" SSRC attribute tuples.
* @param {string} section
* @returns {Array<[SSRC, MediaStreamID, Track.ID]>}
*/
function createUnifiedPlanSSRCAttributeTuples(section) {
const [streamId, trackId] = flatMap(getMatches(section, '^a=msid:(.+) (.+)$'));
const ssrcs = flatMap(getMatches(section, '^a=ssrc:(.+) cname:.+$'));
return ssrcs.map(ssrc => [ssrc, streamId, trackId]);
}
/**
* Create a Map of MediaStreamTrack IDs and their {@link TrackAttributes}.
* @param {string} section - SDP media section
* @param {'planb' | 'unified'} sdpFormat
* @returns {Map<Track.ID, TrackAttributes>}
*/
function createTrackIdsToAttributes(section) {
function createTrackIdsToAttributes(section, sdpFormat) {
const simSSRCs = getSimulcastSSRCs(section);
const ssrcAttrTuples = getMatches(section, '^a=ssrc:([0-9]+) msid:([^\\s]+) ([^\\s]+)$');
const rtxPairs = getSSRCRtxPairs(section);
const ssrcAttrTuples = createSSRCAttributeTuples(section, sdpFormat);

@@ -203,2 +237,3 @@ return ssrcAttrTuples.reduce((trackIdsToSSRCs, tuple) => {

* @param {string} section - SDP media section
* @param {'planb' | 'unified'} sdpFormat
* @param {Map<Track.ID, TrackAttributes>} trackIdsToAttributes - Existing

@@ -208,4 +243,4 @@ * map which will be updated for new MediaStreamTrack IDs

*/
function setSimulcastInMediaSection(section, trackIdsToAttributes) {
const newTrackIdsToAttributes = createTrackIdsToAttributes(section);
function setSimulcastInMediaSection(section, sdpFormat, trackIdsToAttributes) {
const newTrackIdsToAttributes = createTrackIdsToAttributes(section, sdpFormat);
const newTrackIds = Array.from(newTrackIdsToAttributes.keys());

@@ -235,7 +270,18 @@ let trackIds = Array.from(trackIdsToAttributes.keys());

// "relevantSdpLines" are removed.
const sectionLines = new Set(section.split('\r\n').concat(relevantSdpLines));
return flatMap(sectionLines).join('\r\n');
const sectionLines = flatMap(new Set(section.split('\r\n').concat(relevantSdpLines)));
const xGoogleFlagConference = 'a=x-google-flag:conference';
if (!section.match(xGoogleFlagConference)) {
sectionLines.push(xGoogleFlagConference);
}
return sectionLines.join('\r\n');
}
/**
* String representing a MediaStream ID.
* @typedef {string} MediaStreamID
*/
/**
* String representing the SSRC of a MediaStreamTrack.

@@ -242,0 +288,0 @@ * @typedef {string} SSRC

@@ -5,3 +5,3 @@ {

"description": "Twilio Video JavaScript library",
"version": "2.0.0-beta2",
"version": "2.0.0-beta3",
"homepage": "https://twilio.com",

@@ -120,3 +120,3 @@ "author": "Mark Andrus Roberts <mroberts@twilio.com>",

"@twilio/sip.js": "^0.7.7",
"@twilio/webrtc": "^3.0.0",
"@twilio/webrtc": "^3.1.0",
"ws": "^3.3.1",

@@ -123,0 +123,0 @@ "xmlhttprequest": "^1.8.0"

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

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

SocketSocket SOC 2 Logo

Product

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

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc