UNPKG

podchat-browser

Version:

Javascript SDK to use POD's Chat Service - Browser Only

1,203 lines (1,081 loc) 75 kB
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