@virusonic/react-native-sdk
Version:
249 lines (215 loc) • 8.35 kB
JavaScript
var EventBus = require('../util/event-bus');
var Constants = require('../util/datatype');
Call = function (callInfo, options, loggedUser, incoming) {
EventBus.apply(this, arguments);
var me = this;
me._callInfo = callInfo;
me._options = options;
me._worker = null;
me._userWorkers = {};
me.id = callInfo.id;
me.loggedUser = loggedUser;
me.participants = {};
//todo remove owner participant and receive from server
me.participants[loggedUser.username] = {
username: loggedUser.username,
role: 40,
joinOrdinal: 1
};
callInfo.participants.forEach(p => {
me.participants[p.username] = p;
if ((p.joinOrdinal || !incoming) && p.username !== loggedUser.username) {
me.createOrGetWorker(p.username);
}
});
};
Call.prototype = Object.create(EventBus.prototype);
Call.prototype.constructor = Call;
Call.prototype.addParticipant = function (participant) {
const me = this;
me.participants[participant.username] = participant;
}
Call.prototype.removeParticipant = function (participant) {
const me = this;
let username = participant.username;
delete me.participants[username];
if (me._userWorkers && me._userWorkers[username]) {
me._userWorkers[username].destroy();
delete me._userWorkers[username];
}
}
Call.prototype.joinedParticipant = function (participant) {
const me = this;
me.participants[participant.username] = participant;
let myUsername = me.loggedUser.username;
let myParticipant = me.participants[myUsername];
//if my joined event received later than another user joine event, we need iterate for all participants which joined early
//todo refactor without iterate
console.log("My Participant ", myParticipant);
for (const [username, p] of Object.entries(me.participants)) {
console.log("Participant ", p);
if (p.joinOrdinal > 0 && myParticipant.joinOrdinal > 0 && myParticipant.joinOrdinal > p.joinOrdinal && p.username !== myUsername) {
me.createOrGetWorker(p.username);
}
}
}
//todo create method leftParticipant
Call.prototype.startConnectivity = function (stream) {
var me = this;
var promises = [];
if (stream) {
//todo store stream in worker
me._stream = stream;
}
const usersSessionInfo = {};
let needSend = false;
//todo add stream only one peerconnection or create configuration
for (const [username, worker] of Object.entries(me._userWorkers)) {
//todo refactor with stream
try {
console.log("Check need connectivity for worker", username);
if (worker.waitConnectivity() && me._stream) {
console.log("Start connectivity for worker with username", username, me._stream);
promises.push(worker.startConnectivity(me._stream).then(sessionInfo => {
if (sessionInfo) {
needSend = true;
console.log("For " + username + " received sessionInfo: ", sessionInfo);
usersSessionInfo[username] = sessionInfo;
}
}));
}
} catch (e) {
console.log("Error on start connectivity", e);
}
}
return Promise.all(promises).then(() => {
console.log("All workers ready needSend:" + needSend + "; usersSessionInfo:", usersSessionInfo);
if (needSend) {
return {usersSessionInfo: usersSessionInfo};
} else {
return null;
}
});
};
Call.prototype.updateConnectionInfo = function (connectionInfo) {
var me = this;
var sessionInfo = connectionInfo.sessionInfo;
if (sessionInfo) {
var worker = me.createOrGetWorker();
if (sessionInfo.sdpInfo) {
worker.setRemoteSessionInfo(sessionInfo);
}
}
for (var x in connectionInfo.usersSessionInfo) {
var userWorker = me.createOrGetWorker(x);
var userSessionInfo = connectionInfo.usersSessionInfo[x];
if (userSessionInfo.sdpInfo) {
userWorker.setRemoteSessionInfo(userSessionInfo);
}
}
};
Call.prototype.muteAudio = function (mute) {
var me = this;
const tracksInfo = [];
me._stream?.getAudioTracks().forEach(track => {
track.enabled = !mute;
const trackInfo = {id: track.id, type: track.kind, enabled: track.enabled};
tracksInfo.push(trackInfo);
});
return tracksInfo;
}
Call.prototype.muteVideo = function (mute) {
var me = this;
const tracksInfo = [];
me._stream?.getVideoTracks().forEach(track => {
track.enabled = !mute;
const trackInfo = {id: track.id, type: track.kind, enabled: track.enabled};
tracksInfo.push(trackInfo);
});
return tracksInfo;
}
Call.prototype.addExtraSessionDescription = function (extraConnectionInfo) {
var me = this;
if (extraConnectionInfo.extraSDPInfo && me._worker) {
me._worker.addExtraSdpInfo(extraConnectionInfo.extraSDPInfo);
}
let usersExtraSDPInfo = extraConnectionInfo.usersExtraSDPInfo;
if (usersExtraSDPInfo) {
for (const username in usersExtraSDPInfo) {
// skip loop if the property is from prototype
if (!usersExtraSDPInfo.hasOwnProperty(username)) continue;
if (me._userWorkers && me._userWorkers[username]) {
me._userWorkers[username].addExtraSdpInfo(usersExtraSDPInfo[username]);
}
}
}
}
Call.prototype.createOrGetWorker = function (username) {
var me = this;
if (username) {
var userWorker = me._userWorkers[username];
if (!userWorker) {
console.log("Create worker for ", username);
userWorker = me.createWorker(username);
me._userWorkers[username] = userWorker;
}
return userWorker;
} else {
if (!me._worker) {
me._worker = me.createWorker();
}
return me._worker;
}
};
Call.prototype.createWorker = function (username) {
var me = this;
const WebRTCWorker = require('./webrtc-worker');
var webRTCWorker = new WebRTCWorker(me._options);
webRTCWorker.register(Constants.WorkerEvent.iceCandidate, function (candidate) {
me.post(Constants.CallEvent.onIceCandidate, [me.id, username, candidate]);
});
webRTCWorker.register(Constants.WorkerEvent.onTrack, function (event) {
me.post(Constants.CallEvent.onTrack, [username, event]);
});
webRTCWorker.register(Constants.WorkerEvent.trackInfo, function (tracksInfo) {
me.post(Constants.CallEvent.trackInfo, [me.participants[username], tracksInfo]);
});
webRTCWorker.register(Constants.WorkerEvent.connectionState, function (connectionState) {
const participant = me.participants[username];
if (connectionState === "connectionState.failed" || connectionState === "connectionState.disconnected") {
setTimeout(() => {
if (participant && (participant.connectionState === "connectionState.failed" || participant.connectionState === "connectionState.disconnected")) {
//todo leave if for all workers connection failed
me.post(Constants.CallEvent.dead, [username, connectionState]);
}
}, 5000);
}
if (participant) {
participant.connectionState = connectionState;
}
me.post(Constants.CallEvent.connectionState, [username, connectionState]);
});
return webRTCWorker;
};
Call.prototype.destroy = function () {
var me = this;
if (me._worker) {
me._worker.destroy();
me._worker = null;
}
for (const username in me._userWorkers) {
// skip loop if the property is from prototype
if (!me._userWorkers.hasOwnProperty(username)) continue;
me._userWorkers[username].destroy();
}
me._userWorkers = {};
//todo refactor manipulate with stream (destroy stream by configuration)
if (me._stream) {
me._stream.getTracks().forEach((track) => {
//todo configurable destroy track by configuration call
track.stop();
//track.enabled = true;
});
}
}
module.exports = Call;