podchat-browser
Version:
Javascript SDK to use POD's Chat Service - Browser Only
1,203 lines (1,081 loc) • 75 kB
JavaScript
import CallUsers from "./callUsers";
import Utility from "../../../utility/utility";
import CallServerManager from "../callServerManager";
import {callLog, callMetaDataTypes} from "../../constants";
import {errorList} from "../../errorHandler";
import PeerConnectionManager from "./peerConnectionManager";
import ScreenShareStateManager from "../screenShareStateManager"
import MessageExecutorQueue from "../../store/messageExecutorQueue";
function MultiTrackCallManager({app, callId, callConfig}) {
const config = {
callId,
callConfig,
users: new CallUsers({app, callId}),
callServerController: new CallServerManager(app),
screenShareInfo: new ScreenShareStateManager(app),
sendPeerManager: null,
receivePeerManager: null,
receivePeerSdpVersion: 0,
isReconnecting: false,
isReceivePeerReconnecting: false,
isDestroyed: false,
onChatReadyUniqueId: null,
inquiryCallCounter: 0,
receivePeerReConnectTimes: 0,
ti: atob(callConfig.tInfo).split(":")
};
const publicized = {
processCallMessage,
requestReceivingMedia,
callStop,
endCall: app.call.endCall,
screenShareInfo: config.screenShareInfo,
getCallDetails,
sendCallMessage,
callServerController() {
return config.callServerController
},
callConfig() {
return config.callConfig;
},
users() {
return config.users;
},
getCallId() {
return config.callId;
},
setReceivePeerSdpVersion(sdpVersion) {
config.receivePeerSdpVersion = sdpVersion;
},
getReceivePeerSdpVersion(sdpVersion) {
return config.receivePeerSdpVersion;
},
sendPeerManager() {
return config.sendPeerManager;
},
receivePeerManager() {
return config.receivePeerManager;
},
sendCallLog
};
config.callMessageExecutor = new MessageExecutorQueue({
app,
executorTimeout: 10,
callback: publicized.processCallMessage
});
function socketConnectListener() {
if (!config.inquiryCallCounter) {
let dataChangeDetected = false;
config.inquiryCallCounter++;
setTimeout(()=>{
config.inquiryCallCounter = 0;
if(!config.isDestroyed && ((config.receivePeerManager && config.receivePeerManager.isPeerFailed()) || (config.sendPeerManager && config.sendPeerManager.isPeerFailed()))) {
socketConnectListener();
}
}, 9000);
config.isReconnecting = true;
app.call.inquiryCallParticipants.inquiryCallParticipants({}, async result => {
if (!result.hasError) {
app.chatEvents.fireEvent("callEvents", {
type: "INQUIRY_CALL_RESULT",
callId: config.callId,
result
});
if(result.result?.callParticipantVOs && result.result.callParticipantVOs.length) {
let serverUsers = result.result?.callParticipantVOs;
//Sync local users with server users
serverUsers.forEach(item => {
if(!config.users.userExists(item.userId)) {
config.users.addItem({
clientId: item.participantVO.ssoId,
topicSend: item.sendTopic,
mute: item.mute,
video: item.video,
userId: item.userId,
callId : config.callId,
cameraPaused : false,
brokerAddress : config.callConfig.brokerAddress
});
dataChangeDetected = true;
} else {
config.users.get(item.userId).correctWithServerData(item);
}
});
let serverUserIds = serverUsers.map(item => item.userId);
Object.keys(config.users.getAll()).forEach(key => {
if(!serverUserIds.includes(Number(key)) && key != 'screenShare') {
config.users.removeItem(key);
}
dataChangeDetected = true;
});
}
//TODO: IT gives screenShareUser client id instead of its chat user id,
// It might needs change in future
if(Number(result.result.screenShareUser)) {
let localScreenShare = config.users.get('screenShare')
if(!localScreenShare || (!!localScreenShare && localScreenShare.isOwnerChanged(Number(result.result.screenShareUser)))){
correctWhenScreenOwnerChanged({
inquiryResult: result.result,
dataChangeDetected
});
}
} else if(config.users.userExists('screenShare')) {
await config.users.removeItem('screenShare');
dataChangeDetected = true;
}
// if (Number(result.result.recordingUser)) {
// app.chatEvents.fireEvent('callEvents', {
// type: 'CALL_RECORDING_STARTED',
// callId: config.callId,
// result: {
// id: result.result.recordingUser
// }
// });
// } else if(config.callConfig.recordingOwner) {
// app.chatEvents.fireEvent('callEvents', {
// type: 'STOP_RECORDING_CALL',
// callId: config.callId,
// result: {
// id: config.callConfig.recordingOwner
// }
// });
// }
setTimeout(()=>{
config.isReconnecting = true;
checkPeersState();
}, 500);
} else {
if (result.errorCode == 171) {
let callId = config.callId;
sendCallLog(
callLog.levels.INFO,
callLog.types.GENERAL,
`[SDK_BROWSER][inquiryCallParticipants] End call because of errorCode 171 from chat.`,
);
app.call.endCall({callId: config.callId}, null);
app.callsManager.removeItem(config.callId);
app.chatEvents.fireEvent('callEvents', {
type: 'YOU_DROPPED_FROM_CALL',
callId: callId,
result: {
callId: callId,
userId: app.store.user.get().id,
}
});
} else if (result.errorCode == 163) {
sendCallLog(
callLog.levels.INFO,
callLog.types.GENERAL,
`[SDK_BROWSER][inquiryCallParticipants] End call because of errorCode 163 from chat.`,
);
app.call.endCall({callId: config.callId}, null);
app.chatEvents.fireEvent('callEvents', {
type: 'CALL_ENDED',
callId: config.callId
});
app.callsManager.removeItem(config.callId);
}
}
});
}
}
function sendCallLog(logLevel, logType, log, uniqueId = null) {
sendCallMessage({
id: 'CALL_LOG',
token: app.sdkParams.token,
chatId: config.callId,
clientId: config.users.get(app.store.user.get().id).user().clientId,
logLevel,
logType,
log,
uniqueId: (uniqueId ? uniqueId : Utility.generateUUID())
}, null, {});
}
async function correctWhenScreenOwnerChanged({
inquiryResult,
dataChangeDetected
}) {
let screenOwnerChatId = config.users.findUserIdByClientId(Number(inquiryResult.screenShareUser));
config.callConfig.screenShareOwner = screenOwnerChatId;
config.screenShareInfo.setOwner(config.callConfig.screenShareOwner);
config.screenShareInfo.setIsStarted(true);
// if (config.screenShareInfo.isStarted()) {
config.callConfig.screenShareObject.clientId = Number(inquiryResult.screenShareUser);
config.callConfig.screenShareObject.brokerAddress = config.callConfig.brokerAddress;
// config.screenShareInfo.setOwner(config.callConfig.screenShareOwner);
// }
if(config.users.userExists('screenShare')) {
await config.users.removeItem('screenShare');
dataChangeDetected = true;
}
config.users.addItem(config.callConfig.screenShareObject, "screenShare");
}
function getPeersState() {
let sendPeerFailed = false,
receivePeerFailed = false;
/**
* Consider state new as failed, for when user is reconnecting so much and sdk has created the peer in the previous try
*/
let badStates = ['failed', 'new'];
if(!config.sendPeerManager
|| (config.sendPeerManager
&& (
badStates.includes(config.sendPeerManager.getPeer().peerConnection.connectionState)
|| badStates.includes(config.sendPeerManager.getPeer().peerConnection.iceConnectionState)
)
)
) {
sendPeerFailed = true;
}
if(!config.receivePeerManager
|| (config.receivePeerManager
&& (
badStates.includes(config.receivePeerManager.getPeer().peerConnection.connectionState)
|| badStates.includes(config.receivePeerManager.getPeer().peerConnection.iceConnectionState))
)
) {
receivePeerFailed = true;
}
return {
receiveFailed: receivePeerFailed,
sendFailed: sendPeerFailed
}
}
function checkPeersState() {
let peersState = getPeersState();
let failed = {sendPeer: false, receivePeer: false};
if(peersState.receiveFailed) {
failed.receivePeer = true;
} else {
checkReceivePeerStateWithJanus();
}
if(peersState.sendFailed) {
failed.sendPeer = true;
} else if (config.screenShareInfo.iAmOwner() && config.users.get('screenShare')) {
//TODO: Needs improvement
// config.sendPeerManager.removeTrack(`screen-Share-${config.callId}`);
// setTimeout(()=>{
// config.users.get('screenShare').startVideo();
// }, 2000);
}
if(failed.sendPeer || failed.receivePeer) {
reconnectFailedPeers(failed);
}
}
function checkReceivePeerStateWithJanus() {
config.receivePeerManager.resetSendQueue();
config.isReconnecting = false;
new Promise(resolve => {
sendCallMessage({
id: 'REQUEST_RECEIVING_MEDIA',
token: app.sdkParams.token,
chatId: config.callId,
brokerAddress: config.callConfig.brokerAddress
}, null
// result => {
// if(result.done == false)
// return
//
// if (result && result.recvList && result.recvList.length) {
// try {
// let processedTopics = [];
//
// let list = JSON.parse(result.recvList);
// app.logger.log(app.logger.tags.CALL_PROCESS.id,
// `[media][RECEIVING_MEDIA] `,
// `Received.. , clientIds: ${list.map(it => it.clientId).join(', ')} ||| topics: ${list.map(it => it.topic).join(', ')}`
// );
//
// let localCallUsers = config.users.getAll();
// Object.keys(localCallUsers)
// //TODO: add screenshare check
// .forEach(userId => {
//
// if (userId == 'screenShare' ) {
// if(
// localCallUsers[userId].isVideoOpen()
// && !config.screenShareInfo.iAmOwner()
// && !list.find(item => item.topic == `screen-Share-${config.callId}`)
// ) {
// config.receivePeerManager.removeTrack(`screen-Share-${config.callId}`);
// localCallUsers[userId].destroyVideo();
// }
// } else {
// if (localCallUsers[userId].isVideoOpen() && !list.find(item => item.topic == `Vi-send-${config.callId}-${localCallUsers[userId].user().clientId}`)) {
// config.receivePeerManager.removeTrack(`Vi-send-${config.callId}-${localCallUsers[userId].user().clientId}`);
// localCallUsers[userId].destroyVideo();
// }
//
// if (localCallUsers[userId].isAudioOpen() && !list.find(item => item.topic == `Vo-send-${config.callId}-${localCallUsers[userId].user().clientId}`)) {
// config.receivePeerManager.removeTrack(`Vi-send-${config.callId}-${localCallUsers[userId].user().clientId}`);
// localCallUsers[userId].destroyAudio();
// }
// }
// });
// } catch (error) {
// console.error('Unable to parse receive list', error);
// }
// }
//
// resolve();
//
// }
, {timeoutTime: 2000, timeoutRetriesCount: 2});
})
// .then(() => {
// new Promise(resolve => {
// sendCallMessage({
// id: 'REQUEST_LATEST_SDP_OFFER',
// token: app.sdkParams.token,
// chatId: config.callId,
// sdpVersion: config.receivePeerSdpVersion,
// clientId: config.users.get(app.store.user.get().id).user().clientId
// }, result => {
// if (result.sdpVersion == config.receivePeerSdpVersion) {}
// else if (result.unfinishedTopic) {
// result.topic = result.unfinishedTopic;
// handleProcessSdpOffer(result);
// resolve();
// } else if (result.sdpOffer) {
// handleProcessLatestSdpOffer(result);
// }
//
// setTimeout(()=> {
// config.isReconnecting = false;
// sendCallMessage({
// id: 'REQUEST_RECEIVING_MEDIA',
// token: app.sdkParams.token,
// chatId: config.callId,
// brokerAddress: config.callConfig.brokerAddress
// }, null, {})
// },2000);
// }, {timeoutTime: 2000, timeoutRetriesCount: 2});
// });
// });
}
function onSendTrackFailed(){
reconnectFailedPeers({});
}
function reconnectFailedPeers({sendPeer = true, receivePeer = true}) {
let sendPeerFailed = false,
receivePeerFailed = false;
config.isReconnecting = true;
if(sendPeer) {
sendPeerFailed = true;
}
if(receivePeer) {
receivePeerFailed = true;
config.isReceivePeerReconnecting = true;
}
// let badStates = ['failed', 'new'];
// if(!config.sendPeerManager
// || (config.sendPeerManager
// && badStates.includes(config.sendPeerManager.getPeer().peerConnection.connectionState)
// )
// ) {
// sendPeerFailed = true;
// // destroyPeerManager('send');
// }
// console.log('xxx sendPeerManager', config.sendPeerManager.getPeer().peerConnection.connectionState)
//
// if(!config.receivePeerManager
// || (config.receivePeerManager
// && badStates.includes(config.receivePeerManager.getPeer().peerConnection.connectionState)
// )
// ) {
// receivePeerFailed = true;
// // destroyPeerManager('receive');
// }
if(receivePeerFailed) {
app.chatEvents.fireEvent('callEvents', {
type: 'PEER_RECONNECTING',
callId: config.callId,
direction: 'receive'
});
config.users.stopAllReceivers();
}
if(sendPeerFailed) {
app.chatEvents.fireEvent('callEvents', {
type: 'PEER_RECONNECTING',
callId: config.callId,
direction: 'send'
});
config.users.stopAllSenders(false);
}
setTimeout(() => {
sendPeerFailed && destroyPeerManager('send');
receivePeerFailed && destroyPeerManager('receive');
publicized.relocateResources({releaseSend: sendPeerFailed, releaseReceive: receivePeerFailed}).then(result => {
setTimeout(() => {
receivePeerFailed && createPeerManager('receive');
sendPeerFailed && createPeerManager('send');
setTimeout(() => {
if (sendPeerFailed) {
config.users.startAllsenders();
}
if (receivePeerFailed) {
Object.values(config.users.getAll()).forEach(user => {
if (!user.isMe()) {
user.resetTopicVersions();
}
});
}
setTimeout(()=>{
config.isReceivePeerReconnecting = false;
config.isReconnecting = false;
requestReceivingMedia();
}, 20);
// else {
// config.receivePeerManager.repairMutedTracks();
// // requestReceivingMedia();
// config.isReconnecting = false;
// sendCallMessage({
// id: 'REQUEST_LATEST_SDP_OFFER',
// token: app.sdkParams.token,
// chatId: config.callId,
// sdpVersion: config.receivePeerSdpVersion,
// clientId: config.users.get(app.store.user.get().id).user().clientId
// }, null, {});
// }
}, 200);
}, 200);
});
}, 300)
}
function onPeerFailed() {
if (app.messenger.chatState) {
socketConnectListener();
}
}
function checkTURNServer(turnIp, port, useUDP = false, username = '', password = '', timeout) {
let url = 'turn:' + turnIp + ':' + port + '?transport=' + (useUDP ? 'udp' : 'tcp');
const turnConfig = {
urls: url,
username: username,
credential: password
};
if (navigator.userAgent.indexOf('firefox') !== -1 && navigator.userAgent.indexOf('92.0.5') !== -1) {
alert('Browser version is not suitable for video call. Upgrade or use another browser.');
}
return new Promise((resolve) => {
let promiseResolved = false;
setTimeout(() => {
if (!promiseResolved) {
promiseResolved = true;
resolve(false);
}
}, timeout || 5000);
const pc = new RTCPeerConnection({ iceServers: [turnConfig] });
pc.createDataChannel("test"); // اسم خالی نذار
pc.createOffer().then(sdp => {
if (sdp.sdp.indexOf('typ relay') > -1) {
promiseResolved = true;
resolve(true);
}
return pc.setLocalDescription(sdp);
}).catch(() => resolve(false));
pc.onicecandidate = (ice) => {
if (promiseResolved || !ice.candidate || !ice.candidate.candidate) return;
if (ice.candidate.candidate.indexOf('typ relay') > -1) {
promiseResolved = true;
resolve(true);
}
};
});
}
function destroyPeerManager(direction) {
if(direction === 'send' ) {
if(config.sendPeerManager) {
config.sendPeerManager.destroy();
config.sendPeerManager = null;
}
} else {
if(config.receivePeerManager) {
config.receivePeerManager.destroy();
config.receivePeerManager = null;
config.users.resetTopicVersions();
config.receivePeerSdpVersion = 0;
}
}
}
function createPeerManager(direction) {
if(direction == 'send') {
config.sendPeerManager = new PeerConnectionManager({
app,
callId,
direction: 'send',
rtcPeerConfig: {
iceServers: publicized.getTurnServer(publicized.callConfig()),
iceTransportPolicy: 'relay',
},
brokerAddress: config.callConfig.brokerAddress,
onPeerFailed,
onSendTrackFailed
});
} else {
config.receivePeerManager = new PeerConnectionManager({
app,
callId,
direction: 'receive',
rtcPeerConfig: {
iceServers: publicized.getTurnServer(publicized.callConfig()),
iceTransportPolicy: 'relay',
},
brokerAddress: config.callConfig.brokerAddress,
onPeerFailed
});
}
}
function startCallWebRTCFunctions(callConfig) {
config.callServerController.setServers(callConfig.kurentoAddress);
createPeerManager('send');
createPeerManager('receive');
config.onChatReadyUniqueId = app.chatEvents.on('chatReady', socketConnectListener);
new Promise(async resolve => {
let callVideo = (typeof callConfig.video === 'boolean') ? callConfig.video : true,
callMute = (typeof callConfig.mute === 'boolean') ? callConfig.mute : false;
if (callConfig.selfData) {
callConfig.selfData.callId = config.callId;
callConfig.selfData.cameraPaused = callConfig.cameraPaused;
callConfig.selfData.brokerAddress = config.callConfig.brokerAddress;
config.users.addItem(callConfig.selfData);
// callStateController.setupCallParticipant(params.selfData);
}
config.screenShareInfo.setOwner(callConfig.screenShareOwner);
config.screenShareInfo.setIsStarted(!!callConfig.screenShareOwner);
if (callConfig.recordingOwner) {
app.chatEvents.fireEvent('callEvents', {
type: 'CALL_RECORDING_STARTED',
callId: config.callId,
result: {
id: callConfig.recordingOwner
}
});
}
if (callConfig.clientsList && callConfig.clientsList.length) {
for (let i in callConfig.clientsList) {
if (callConfig.clientsList[i].userId !== app.store.user.get().id) {
callConfig.clientsList[i].callId = config.callId;
callConfig.clientsList[i].cameraPaused = false;
callConfig.clientsList[i].brokerAddress = config.callConfig.brokerAddress;
config.users.addItem(callConfig.clientsList[i]);
}
}
}
config.callConfig.screenShareObject = {
callId: config.callId,
cameraPaused: false,
userId: "screenShare",
topicSend: callConfig.screenShare
};
config.screenShareInfo.setIsStarted(!!config.callConfig.screenShareOwner);
if (config.screenShareInfo.isStarted()) {
let clientId = callConfig.screenShare.split('-')[2];
let screenOwnerClientId;
for (let user of callConfig.clientsList) {
if (user.userId == config.callConfig.screenShareOwner) {
screenOwnerClientId = user.clientId;
}
}
config.callConfig.screenShareObject.clientId = screenOwnerClientId;
config.callConfig.screenShareObject.brokerAddress = config.callConfig.brokerAddress;
config.screenShareInfo.setOwner(config.callConfig.screenShareOwner);
config.users.addItem(config.callConfig.screenShareObject, "screenShare");
}
config.callConfig.callVideo = callVideo;
config.callConfig.callAudio = callMute;
let callConf = publicized.callConfig(),
serversTemp = app.call.sharedVariables.useInternalTurnAddress ? callConf.internalTurnAddress.split(',') : callConf.turnAddress.split(','),
turnCheckRes = false;
if(app.call.sharedVariables.useCustomTurnAddress) {
serversTemp = [app.call.sharedVariables.callTurnIp];
}
if(serversTemp && serversTemp.length) {
new Promise(async (resolve, reject) => {
for(let i in serversTemp) {
turnCheckRes = await checkTURNServer(
serversTemp[i].split(":")[0],
'3478',
true,
config.ti[0],
config.ti[1],
2000
);
if(turnCheckRes) {
createSessionInChat();
return resolve();
}
}
if(!turnCheckRes) {
publicized.raiseCallError(errorList.TURN_SERVERS_NOT_ACCESSIBLE, null, true);
sendCallLog(
callLog.levels.ERROR,
callLog.types.TRACK_FLOW,
`[SDK_BROWSER][turnsInaccessible] Failed to connect to turn servers.`,
);
app.call.endCall({callId: config.callId});
return reject();
}
})
} else {
publicized.raiseCallError(errorList.TURN_SERVERS_NOT_ACCESSIBLE, null, true);
app.call.endCall({callId: config.callId});
}
resolve();
}).then(() => {
app.call.currentCall().sendCallDivs()
})
}
function createSessionInChat() {
app.call.callStopQueue.callStarted = true;
let message = {
id: 'CREATE_SESSION',
brokerAddress: config.callConfig.brokerAddress,
turnAddress: config.callConfig.turnAddress.split(',')[0],
chatId: callId,
// clientId: app.sdkParams.token
token: app.sdkParams.token,
},
onResultCallback = function (res) {
if (res.done === 'TRUE') {
app.call.callStopQueue.callStarted = true;
let user = config.users.get(app.store.user.get().id);
//Start my own senders
if (user.user().video) {
user.startVideo(user.user().topicSend, null);
}
if (!user.user().mute) {
user.startAudio(user.user().topicSend, null);
}
} else {
publicized.raiseCallError(errorList.CREATE_SESSION_TIMEOUT, null, true);
app.chatEvents.fireEvent('callEvents', {
type: 'YOU_DROPPED_FROM_CALL',
callId: app.call.currentCall().getCallId(),
result: {
callId: app.call.currentCall().getCallId(),
userId: app.store.user.get().id,
}
});
app.call.endCall({callId: app.call.currentCall().getCallId()});
}
};
sendCallMessage(message, onResultCallback, {
timeoutTime: 4000,
timeoutRetriesCount: 5
}
)
}
async function callStop(resetCurrentCallId = true, resetCameraPaused = true) {
await config.users.destroy();
config.sendPeerManager && config.sendPeerManager.destroy();
config.receivePeerManager && config.receivePeerManager.destroy();
if (app.call.callStopQueue.callStarted) {
sendCallMessage({
id: 'EXIT_CLIENT',
token: app.sdkParams.token
}, null, {});
app.call.callStopQueue.callStarted = false;
}
if (resetCameraPaused)
app.call.joinCallParams.cameraPaused = false;
clearTimeout(config.callRequestTimeout);
config.callConfig = {};
if (resetCurrentCallId)
config.callId = null;
}
function sendCallMessage(message, callback, {
timeoutTime = 0,
timeoutRetriesCount = 0
}) {
message.token = app.sdkParams.token;
if (!message.uniqueId) {
message.uniqueId = Utility.generateUUID();
}
message.chatId = config.callId;
let data = {
type: 3,
content: {
peerName: config.callServerController.getCurrentServer(),// callServerName,
priority: 1,
content: JSON.stringify(message),
ttl: app.sdkParams.messageTtl
},
uniqueId: message.uniqueId
};
if (typeof callback == 'function') {
app.store.messagesCallbacks[message.uniqueId] = callback;
}
app.call.sharedVariables.asyncClient.send(data, function (res) {
});
if (timeoutTime || app.call.sharedVariables.globalCallRequestTimeout > 0) {
app.store.asyncRequestTimeouts[message.uniqueId] && clearTimeout(app.store.asyncRequestTimeouts[message.uniqueId]);
app.store.asyncRequestTimeouts[message.uniqueId] = setTimeout(function () {
if (app.store.messagesCallbacks[message.uniqueId]) {
delete app.store.messagesCallbacks[message.uniqueId];
}
if (timeoutRetriesCount) {
app.sdkParams.consoleLogging && console.log("[SDK][sendCallMessage] Retrying call request. uniqueId :" + message.uniqueId, {message})
//timeoutCallback();
sendCallMessage(message, callback, {timeoutTime, timeoutRetriesCount: timeoutRetriesCount - 1})
} else if (typeof callback == 'function') {
/**
* Request failed
*/
callback({
done: 'SKIP'
});
}
}, timeoutTime || app.call.sharedVariables.globalCallRequestTimeout);
}
}
let trackChangeTimeout = null;
function handleReceivingTracksChanges(jsonMessage) {
if(!config.receivePeerManager)
return;
if (jsonMessage && jsonMessage.recvList && jsonMessage.recvList.length) {
try {
let processedTopics = [];
let list = JSON.parse(jsonMessage.recvList);
app.logger.log(app.logger.tags.CALL_PROCESS.id,
`[media][RECEIVING_MEDIA] `,
`Received.. , clientIds: ${list.map(it => it.clientId).join(', ')} ||| topics: ${list.map(it => it.topic).join(', ')}`
);
// console.log('[media][RECEIVING_MEDIA] Received.. , clientIds: ', list.map(it => it.clientId).join(', '),' ||| topics: ', list.map(it => it.topic).join(', '));
for (let i = list.length - 1; i >= 0; i--) {
if (!processedTopics.includes(list[i].topic)) {
processedTopics.push(list[i].topic);
let userId = config.users.findUserIdByTopic(list[i].topic);
let user = config.users.get(userId);
app.logger.log(app.logger.tags.CALL_PROCESS.id,
`[media][RECEIVING_MEDIA][User] `,
` clientId: ${list[i].clientId} , topic: ${list[i].topic} , item: , ${list[i]}, ${{userId, user}}`
);
if (user) {
if (
(user.isScreenShare() && !config.screenShareInfo.iAmOwner())
|| !user.isMe()
) {
user.processTrackChange(list[i]);
}
} else {
console.log('[media][RECEIVING_MEDIA] User not found in the call , clientIds: ', list[i]);
console.error('[SDK][handleReceivingTracksChanges] User not found in the call. ', list[i])
}
}
}
} catch (error) {
console.error('Unable to parse receive list', error);
}
}
}
let prevOffer = null;
function handleProcessSdpOffer(jsonMessage) {
if(!config.receivePeerManager)
return;
config.receivePeerManager.resetSubscribeOrUpdateFailedCount();
config.receivePeerSdpVersion = jsonMessage.sdpVersion;
if(!config.receivePeerManager)
return;
config.receivePeerManager.removeRequestTimeout(jsonMessage.uniqueId);
if (jsonMessage.topic && jsonMessage.topic.length) {
let temp = jsonMessage.sdpOffer.substring(jsonMessage.sdpOffer.indexOf('IP4'))
if(prevOffer == temp) {
console.warn('[media][handleProcessSdpOffer] same offers', {prevOffer}, jsonMessage.sdpOffer)
config.receivePeerManager.handleProcessSDPOfferForReceiveTrackUseExsistingTracks(jsonMessage, null)
} else {
config.receivePeerManager.handleProcessSDPOfferForReceiveTrack(jsonMessage, null);
}
prevOffer = temp;
}
}
function handleProcessSdpNegotiate(jsonMessage){
if(!config.receivePeerManager)
return;
config.receivePeerSdpVersion = jsonMessage.sdpVersion;
config.receivePeerManager.removeRequestTimeout(jsonMessage.uniqueId);
config.receivePeerManager.addNegotiationOfferToQueue(jsonMessage.sdpOffer, jsonMessage.topic);
config.receivePeerManager.maybeNextTrack();
}
let requestReceivingMediaTimeout = null;
function requestReceivingMedia() {
requestReceivingMediaTimeout && clearTimeout(requestReceivingMediaTimeout)
requestReceivingMediaTimeout = setTimeout(()=>{
sendCallMessage({
id: 'REQUEST_RECEIVING_MEDIA',
token: app.sdkParams.token,
chatId: config.callId,
brokerAddress: config.callConfig.brokerAddress
}, null, {});
}, 1000);
}
function handleProcessLatestSdpOffer(jsonMessage) {
if(!config.receivePeerManager)
return;
config.receivePeerSdpVersion = jsonMessage.sdpVersion;
config.receivePeerManager.removeRequestTimeout(jsonMessage.uniqueId);
if (jsonMessage.sdpOffer) {
config.receivePeerManager.addNegotiationOfferToQueue(jsonMessage.sdpOffer, []);
} else {
requestReceivingMedia();
}
}
function handleProcessSdpAnswer(jsonMessage) {
if(!config.sendPeerManager)
return;
let peer = config.sendPeerManager.getPeer();
if (peer == null) {
app.logger.warn(app.logger.tags.CALL_PROCESS.id,
`[${jsonMessage.id}] `,
{
callId: config.callId,
message: " Skip, no WebRTC Peer" ,
info: getCallDetails()
}
);
if(!config.isReconnecting)
reconnectFailedPeers({receivePeer: false});
return;
}
peer.processAnswer(jsonMessage.sdpAnswer, (err) => {
if (err) {
//sendCallSocketError("[handleProcessSdpAnswer] Error: " + err);
sendCallLog(
callLog.levels.ERROR,
callLog.types.TRACK_FLOW,
`[SDK_BROWSER][handleProcessSdpAnswer] Process sdpAnswer failed. Error: ${err}`,
jsonMessage.uniqueId
);
app.logger.error(app.logger.tags.CALL_PROCESS.id,
`[processAnswer] `,
{
callId: config.callId,
message: " Error: " + err,
info: getCallDetails()
}
);
if(!config.isDestroyed)
reconnectFailedPeers({receivePeer: false});
return;
}
config.sendPeerManager.removeRequestTimeout(jsonMessage.uniqueId);
config.sendPeerManager.processingCurrentTrackCompleted();
});
}
function handleSendAddIceCandidate(jsonMessage) {
let peer = config.sendPeerManager.getPeer();
if (!peer) {
app.logger.error(app.logger.tags.CALL_PROCESS.id,
`[handleSendAddIceCandidate] `,
{
callId: config.callId,
message: "Error: Skip, no WebRTC Peer ",
info: getCallDetails()
}
);
return;
}
if (jsonMessage.candidate && jsonMessage.candidate.length) {
let candidate = JSON.parse(jsonMessage.candidate);
config.sendPeerManager.addIceCandidateToQueue(candidate)
}
}
function handleReceiveAddIceCandidate(jsonMessage) {
if(!config.receivePeerManager)
return;
let peer = config.receivePeerManager.getPeer();
if (!peer) {
app.logger.error(app.logger.tags.CALL_PROCESS.id,
`[handleReceiveAddIceCandidate] `,
{
callId: config.callId,
message: "Error: Skip, no WebRTC Peer ",
info: getCallDetails()
}
);
return;
}
if (jsonMessage.candidate && jsonMessage.candidate.length) {
let candidate = JSON.parse(jsonMessage.candidate);
config.receivePeerManager.addIceCandidateToQueue(candidate);
}
}
function getCallDetails(customData) {
return {
currentUser: app.store.user.get(),
isJanus: config.callId && config.callServerController.isJanus(),
screenShareInfo: {
isStarted: config.screenShareInfo.isStarted(),
iAmOwner: config.screenShareInfo.iAmOwner(),
},
callId: config.callId,
startCallInfo: config.callConfig,
...customData,
}
}
function sendCallSocketError(message) {
app.chatEvents.fireEvent('callEvents', {
type: 'CALL_ERROR',
callId: config.callId,
code: 7000,
message: message,
environmentDetails: getCallDetails()
});
sendCallMessage({
id: 'ERROR',
message: message,
}, null, {});
}
function handleReceivedMetaData(jsonMessage, uniqueId) {
let jMessage = JSON.parse(jsonMessage.message);
let id = jMessage.id;
if (!id || typeof id === "undefined" || jsonMessage.userid == app.store.user.get().id) {
return;
}
switch (id) {
case callMetaDataTypes.POORCONNECTION:
publicized.sendQualityCheckEvent({
userId: jMessage.userid,
topic: jMessage.content.description,//jMessage.topic,
mediaType: (jMessage.content.description.indexOf('Vi') !== -1 ? 'video' : 'audio'),//jMessage.mediaType,
canSendCallMetaData: false
});
break;
case callMetaDataTypes.POORCONNECTIONRESOLVED:
publicized.sendQualityCheckEvent({
userId: jMessage.userid,
topic: jMessage.content.description,
mediaType: (jMessage.content.description.indexOf('Vi') !== -1 ? 'video' : 'audio'),
isResolved: true,
canSendCallMetaData: false
});
break;
case callMetaDataTypes.CUSTOMUSERMETADATA:
if (app.store.messagesCallbacks[uniqueId]) {
app.store.messagesCallbacks[uniqueId](jsonMessage);
}
app.chatEvents.fireEvent('callEvents', {
type: 'CUSTOM_USER_METADATA',
callId: config.callId,
userId: jMessage.userid,
content: jMessage.content
});
break;
case callMetaDataTypes.SCREENSHAREMETADATA:
if (config.screenShareInfo.isStarted()) {
config.screenShareInfo.setWidth(jMessage.content.dimension.width);
config.screenShareInfo.setHeight(jMessage.content.dimension.height);
app.chatEvents.fireEvent("callEvents", {
type: 'SCREENSHARE_METADATA',
callId: config.callId,
userId: jMessage.userid,
content: jMessage.content
});
}
break;
}
}
function handleSlowLink(jsonMessage) {
let userId = config.users.findUserIdByClientId(jsonMessage.client)
config.users.get(userId).startSLowLink();
}
function sendCallMetaData(params) {
let message = {
id: params.id,
userid: params.userid,
content: params.content || undefined
};
sendCallMessage({
id: 'SENDMETADATA',
message: JSON.stringify(message),
chatId: config.callId
}, null, {});
}
function handleError(jsonMessage, sendingTopic, receiveTopic) {
const errMessage = jsonMessage.message;
app.chatEvents.fireEvent('callEvents', {
type: 'CALL_ERROR',
callId: config.callId,
code: 7000,
message: "Kurento error: " + errMessage,
environmentDetails: getCallDetails()
});
}
function processCallMessage(message) {
let uniqueId = message.uniqueId;
if (message.done !== 'FALSE' || (message.done === 'FALSE' && message.desc === 'duplicated')) {
app.store.asyncRequestTimeouts[uniqueId] && clearTimeout(app.store.asyncRequestTimeouts[uniqueId]);
} else if (message.done === 'FALSE') {
app.logger.error(app.logger.tags.CALL_SERVER_MESSAGE.id,
`[processCallMessage] `,
{
callId: config.callId,
message: " Server error: " + (message.desc ? message.desc : message.message),
info: getCallDetails()
}
);
app.chatEvents.fireEvent("callEvents", {
type: 'CALL_SERVER_ERROR',
result: message
});
}
app.logger.log(app.logger.tags.CALL_SERVER_MESSAGE.id,
`[${message.id}] `,
message
);
switch (message.id) {
case 'PROCESS_SDP_ANSWER': //For send connection 1
handleProcessSdpAnswer(message);
break;
case 'SEND_COMPLETE': //For send connection 2
// config.sendPeerManager.processingCurrentTrackCompleted();
break;
case 'RECEIVING_MEDIA': // Only for receiving topics from janus, first we subscribe
if (app.store.messagesCallbacks[uniqueId]) {
app.store.messagesCallbacks[uniqueId](message);
} else {
if(!config.isReconnecting) {
handleReceivingTracksChanges(message);
}
}
break;
case 'UNPUBLISHED':
// handleReceivingTracksChanges(message);
break;
case 'PROCESS_SDP_OFFER': //Then janus sends offers
case 'PROCESS_SDP_UPDATE':
config.receivePeerReConnectTimes = 0;
handleProcessSdpOffer(message);
break;
case 'PROCESS_SDP_NEGOTIATE':
config.receivePeerReConnectTimes = 0;
if (app.store.messagesCallbacks[uniqueId]) {
app.store.messagesCallbacks[uniqueId](message);
} else {
handleProcessSdpNegotiate(message);
}
break;
case 'PROCESS_LATEST_SDP_OFFER':
config.receivePeerReConnectTimes = 0;
if (app.store.messagesCallbacks[uniqueId]) {
app.store.messagesCallbacks[uniqueId](message);
} else {
handleProcessLatestSdpOffer(message);
}
break;
case 'SEND_ADD_ICE_CANDIDATE':
handleSendAddIceCandidate(message);
break
case 'RECIVE_ADD_ICE_CANDIDATE':
handleReceiveAddIceCandidate(message);
break;
case 'JOIN_AADDITIONN_COMPLETE': // For receive connections 2
// let topics = JSON.parse(message.topic);
// let recvData = message.addition;
// if(recvData && recvData.length) {
// try {
// recvData = JSON.parse(recvData);
// } catch (error) {
// console.error('Unable to parse JOIN_AADDITIONN_COMPLETE result', error);
// }
// let userId