You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
390 lines
9.5 KiB
390 lines
9.5 KiB
(function(OCA, OC) {
|
|
'use strict';
|
|
|
|
OCA.SpreedMe = OCA.SpreedMe || {};
|
|
|
|
function SignalingBase() {
|
|
this.sessionId = '';
|
|
this.currentCallToken = null;
|
|
this.handlers = {};
|
|
}
|
|
|
|
SignalingBase.prototype.on = function(ev, handler) {
|
|
if (!this.handlers.hasOwnProperty(ev)) {
|
|
this.handlers[ev] = [handler];
|
|
} else {
|
|
this.handlers[ev].push(handler);
|
|
}
|
|
};
|
|
|
|
SignalingBase.prototype.emit = function(/*ev, data*/) {
|
|
// Override in subclasses.
|
|
};
|
|
|
|
SignalingBase.prototype._trigger = function(ev, args) {
|
|
var handlers = this.handlers[ev];
|
|
if (!handlers) {
|
|
return;
|
|
}
|
|
|
|
handlers = handlers.slice(0);
|
|
for (var i = 0, len = handlers.length; i < len; i++) {
|
|
var handler = handlers[i];
|
|
handler.apply(handler, args);
|
|
}
|
|
};
|
|
|
|
SignalingBase.prototype.getSessionid = function() {
|
|
return this.sessionId;
|
|
};
|
|
|
|
SignalingBase.prototype.disconnect = function() {
|
|
this.sessionId = '';
|
|
this.currentCallToken = null;
|
|
};
|
|
|
|
SignalingBase.prototype.emit = function(ev, data) {
|
|
switch (ev) {
|
|
case 'join':
|
|
var callback = arguments[2];
|
|
var token = data;
|
|
this.joinCall(token, callback);
|
|
break;
|
|
case 'leave':
|
|
this.leaveCurrentCall();
|
|
break;
|
|
case 'message':
|
|
this.sendCallMessage(data);
|
|
break;
|
|
}
|
|
};
|
|
|
|
SignalingBase.prototype.leaveCurrentCall = function() {
|
|
if (this.currentCallToken) {
|
|
this.leaveCall(this.currentCallToken);
|
|
this.currentCallToken = null;
|
|
}
|
|
};
|
|
|
|
SignalingBase.prototype.leaveAllCalls = function() {
|
|
// Override if necessary.
|
|
};
|
|
|
|
SignalingBase.prototype.setRoomCollection = function(rooms) {
|
|
this.roomCollection = rooms;
|
|
return this.syncRooms();
|
|
};
|
|
|
|
SignalingBase.prototype.syncRooms = function() {
|
|
var defer = $.Deferred();
|
|
if (this.roomCollection && oc_current_user) {
|
|
this.roomCollection.fetch({
|
|
success: function(data) {
|
|
defer.resolve(data);
|
|
}
|
|
});
|
|
} else {
|
|
defer.resolve([]);
|
|
}
|
|
return defer;
|
|
};
|
|
|
|
// Connection to the internal signaling server provided by the app.
|
|
function InternalSignaling() {
|
|
SignalingBase.prototype.constructor.apply(this, arguments);
|
|
this.spreedArrayConnection = [];
|
|
|
|
this.pingFails = 0;
|
|
this.pingInterval = null;
|
|
|
|
this.sendInterval = window.setInterval(function(){
|
|
this.sendPendingMessages();
|
|
}.bind(this), 500);
|
|
}
|
|
|
|
InternalSignaling.prototype = new SignalingBase();
|
|
InternalSignaling.prototype.constructor = InternalSignaling;
|
|
|
|
InternalSignaling.prototype.disconnect = function() {
|
|
this.spreedArrayConnection = [];
|
|
if (this.source) {
|
|
this.source.close();
|
|
this.source = null;
|
|
}
|
|
if (this.sendInterval) {
|
|
window.clearInterval(this.sendInterval);
|
|
this.sendInterval = null;
|
|
}
|
|
if (this.pingInterval) {
|
|
window.clearInterval(this.pingInterval);
|
|
this.pingInterval = null;
|
|
}
|
|
if (this.roomPoller) {
|
|
window.clearInterval(this.roomPoller);
|
|
this.roomPoller = null;
|
|
}
|
|
SignalingBase.prototype.disconnect.apply(this, arguments);
|
|
};
|
|
|
|
InternalSignaling.prototype.on = function(ev/*, handler*/) {
|
|
SignalingBase.prototype.on.apply(this, arguments);
|
|
|
|
switch (ev) {
|
|
case 'connect':
|
|
// A connection is established if we can perform a request
|
|
// through it.
|
|
this._sendMessageWithCallback(ev);
|
|
break;
|
|
|
|
case 'stunservers':
|
|
case 'turnservers':
|
|
// Values are not pushed by the server but have to be explicitly
|
|
// requested.
|
|
this._sendMessageWithCallback(ev);
|
|
break;
|
|
}
|
|
};
|
|
|
|
InternalSignaling.prototype._sendMessageWithCallback = function(ev) {
|
|
var message = [{
|
|
ev: ev
|
|
}];
|
|
$.post(OC.generateUrl('/apps/spreed/signalling'), {
|
|
messages: JSON.stringify(message)
|
|
}, function(data) {
|
|
this._trigger(ev, [data]);
|
|
}.bind(this));
|
|
};
|
|
|
|
InternalSignaling.prototype.joinCall = function(token, callback, password) {
|
|
// The client is joining a new call, in this case we need
|
|
// to do the following:
|
|
//
|
|
// 1. Join the call as participant.
|
|
// 2. Get a list of other connected clients in the call.
|
|
// 3. Pass information about the clients that need to be called by you to the callback.
|
|
//
|
|
// The clients will then use the message command to exchange
|
|
// their signalling information.
|
|
$.ajax({
|
|
url: OC.linkToOCS('apps/spreed/api/v1/call', 2) + token,
|
|
type: 'POST',
|
|
beforeSend: function (request) {
|
|
request.setRequestHeader('Accept', 'application/json');
|
|
},
|
|
data: {
|
|
password: password
|
|
},
|
|
success: function (result) {
|
|
console.log("Joined", result);
|
|
this.sessionId = result.ocs.data.sessionId;
|
|
this.currentCallToken = token;
|
|
this._startPingCall();
|
|
this._openEventSource();
|
|
this._getCallPeers(token).then(function(peers) {
|
|
var callDescription = {
|
|
'clients': {}
|
|
};
|
|
|
|
peers.forEach(function(element) {
|
|
if (element['sessionId'] < this.sessionId) {
|
|
callDescription['clients'][element['sessionId']] = {
|
|
'video': true
|
|
};
|
|
}
|
|
}.bind(this));
|
|
callback('', callDescription);
|
|
}.bind(this));
|
|
}.bind(this),
|
|
error: function (result) {
|
|
if (result.status === 404 || result.status === 503) {
|
|
// Room not found or maintenance mode
|
|
OC.redirect(OC.generateUrl('apps/spreed'));
|
|
}
|
|
|
|
if (result.status === 403) {
|
|
// Invalid password
|
|
OC.dialogs.prompt(
|
|
t('spreed', 'Please enter the password for this call'),
|
|
t('spreed','Password required'),
|
|
function (result, password) {
|
|
if (result && password !== '') {
|
|
this.joinCall(token, callback, password);
|
|
}
|
|
}.bind(this),
|
|
true,
|
|
t('spreed','Password'),
|
|
true
|
|
).then(function() {
|
|
var $dialog = $('.oc-dialog:visible');
|
|
$dialog.find('.ui-icon').remove();
|
|
|
|
var $buttons = $dialog.find('button');
|
|
$buttons.eq(0).text(t('core', 'Cancel'));
|
|
$buttons.eq(1).text(t('core', 'Submit'));
|
|
});
|
|
}
|
|
}.bind(this)
|
|
});
|
|
};
|
|
|
|
InternalSignaling.prototype.leaveCall = function(token) {
|
|
if (token === this.currentCallToken) {
|
|
this._stopPingCall();
|
|
this._closeEventSource();
|
|
}
|
|
$.ajax({
|
|
url: OC.linkToOCS('apps/spreed/api/v1/call', 2) + token,
|
|
method: 'DELETE',
|
|
async: false
|
|
});
|
|
};
|
|
|
|
InternalSignaling.prototype.sendCallMessage = function(data) {
|
|
if(data.type === 'answer') {
|
|
console.log("ANSWER", data);
|
|
} else if(data.type === 'offer') {
|
|
console.log("OFFER", data);
|
|
}
|
|
this.spreedArrayConnection.push({
|
|
ev: "message",
|
|
fn: JSON.stringify(data),
|
|
sessionId: this.sessionId
|
|
});
|
|
};
|
|
|
|
InternalSignaling.prototype.setRoomCollection = function(/*rooms*/) {
|
|
this._pollForRoomChanges();
|
|
return SignalingBase.prototype.setRoomCollection.apply(this, arguments);
|
|
};
|
|
|
|
InternalSignaling.prototype._pollForRoomChanges = function() {
|
|
if (this.roomPoller) {
|
|
window.clearInterval(this.roomPoller);
|
|
}
|
|
this.roomPoller = window.setInterval(function() {
|
|
this.syncRooms();
|
|
}.bind(this), 10000);
|
|
};
|
|
|
|
/**
|
|
* @private
|
|
*/
|
|
InternalSignaling.prototype._getCallPeers = function(token) {
|
|
var defer = $.Deferred();
|
|
$.ajax({
|
|
beforeSend: function (request) {
|
|
request.setRequestHeader('Accept', 'application/json');
|
|
},
|
|
url: OC.linkToOCS('apps/spreed/api/v1/call', 2) + token,
|
|
success: function (result) {
|
|
var peers = result.ocs.data;
|
|
defer.resolve(peers);
|
|
}
|
|
});
|
|
return defer;
|
|
};
|
|
|
|
/**
|
|
* @private
|
|
*/
|
|
InternalSignaling.prototype._openEventSource = function() {
|
|
// Connect to the messages endpoint and pull for new messages
|
|
this.source = new OC.EventSource(OC.generateUrl('/apps/spreed/messages'));
|
|
|
|
this.source.listen('usersInRoom', function(users) {
|
|
this._trigger('usersInRoom', [users]);
|
|
}.bind(this));
|
|
this.source.listen('message', function(message) {
|
|
if (typeof(message) === 'string') {
|
|
message = JSON.parse(message);
|
|
}
|
|
this._trigger('message', [message]);
|
|
}.bind(this));
|
|
this.source.listen('__internal__', function(data) {
|
|
if (data === 'close') {
|
|
console.log('signaling connection closed - will reopen');
|
|
setTimeout(function() {
|
|
this._openEventSource();
|
|
}.bind(this), 0);
|
|
}
|
|
}.bind(this));
|
|
};
|
|
|
|
/**
|
|
* @private
|
|
*/
|
|
InternalSignaling.prototype._closeEventSource = function() {
|
|
if (this.source) {
|
|
this.source.close();
|
|
this.source = null;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @private
|
|
*/
|
|
InternalSignaling.prototype.sendPendingMessages = function() {
|
|
if (!this.spreedArrayConnection.length) {
|
|
return;
|
|
}
|
|
|
|
$.post(OC.generateUrl('/apps/spreed/signalling'), {
|
|
messages: JSON.stringify(this.spreedArrayConnection)
|
|
});
|
|
this.spreedArrayConnection = [];
|
|
};
|
|
|
|
/**
|
|
* @private
|
|
*/
|
|
InternalSignaling.prototype._startPingCall = function() {
|
|
this._pingCall();
|
|
|
|
// Send a ping to the server all 5 seconds to ensure that the connection
|
|
// is still alive.
|
|
this.pingInterval = window.setInterval(function() {
|
|
this._pingCall();
|
|
}.bind(this), 5000);
|
|
};
|
|
|
|
/**
|
|
* @private
|
|
*/
|
|
InternalSignaling.prototype._stopPingCall = function() {
|
|
if (this.pingInterval) {
|
|
window.clearInterval(this.pingInterval);
|
|
this.pingInterval = null;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @private
|
|
*/
|
|
InternalSignaling.prototype._pingCall = function() {
|
|
if (!this.currentCallToken) {
|
|
return;
|
|
}
|
|
|
|
$.ajax({
|
|
url: OC.linkToOCS('apps/spreed/api/v1/call', 2) + this.currentCallToken + '/ping',
|
|
method: 'POST'
|
|
}).done(function() {
|
|
this.pingFails = 0;
|
|
}.bind(this)).fail(function(xhr) {
|
|
// If there is an error when pinging, retry for 3 times.
|
|
if (xhr.status !== 404 && this.pingFails < 3) {
|
|
this.pingFails++;
|
|
return;
|
|
}
|
|
OCA.SpreedMe.Calls.leaveCurrentCall(false);
|
|
}.bind(this));
|
|
};
|
|
|
|
OCA.SpreedMe.createSignalingConnection = function() {
|
|
// TODO(fancycode): Create different type of signaling connection
|
|
// depending on configuration.
|
|
return new InternalSignaling();
|
|
};
|
|
|
|
})(OCA, OC);
|