UNPKG

podchat-browser

Version:

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

1,180 lines (1,083 loc) 46.8 kB
import Utility from "../../../utility/utility"; import {errorList} from "../../errorHandler"; function CallUser(app, user) { const config = { callId: user.callId, userId: user.userId, user, isMe: user.userId == app.store.user.get().id, containerTag: null, htmlElements: {}, videoIsOpen: false, audioIsOpen: false, topicMetaData: { audioLevelInterval: null }, slowLinkTimeout: null, isDestroyed: false, videoReceiveVersion: -1, audioReceiveVersion: -1, videoStream: null, audioStream: null, streamTracks: { video: null, audio: null, }, oldAudioStreamId: null, newAudioStreamId: null }; const publicized = { getAudioStream() { return config.audioStream }, getVideoStream() { return config.videoStream }, async switchSpeaker(deviceId) { try { const audioObject = publicized.getAudioObject(); if (audioObject) { audioObject.setSinkId(deviceId); app.sdkParams.consoleLogging && console.log("speaker changed") } } catch (error) { console.error('Error setting audio output:', error); } }, audioIsOpen(){ return config.audioIsOpen; }, resetTopicVersions() { config.videoReceiveVersion = -1; config.audioReceiveVersion = -1; }, getAudioObject() { return config.audioObject; }, userId() { return config.userId; }, isMe() { return config.userId == app.store.user.get().id; }, setVideoIsOpen(value) { config.videoIsOpen = value; }, setAudioIsOpen(value) { config.audioIsOpen = value; }, isScreenShare() { return false; }, user() { return config.user; }, getHTMLElements() { return config.htmlElements; }, getVideoHtmlElement() { let elementUniqueId = Utility.generateUUID(); if (config.user.video && !config.htmlElement) { config.htmlElement = document.createElement('video'); let el = config.htmlElement; el.setAttribute('id', 'callUserVideo-' + config.user.videoTopicName); el.setAttribute('class', app.call.sharedVariables.callVideoTagClassName); el.setAttribute('playsinline', ''); el.setAttribute('muted', ''); el.setAttribute('autoplay', ''); el.setAttribute('data-uniqueId', elementUniqueId); el.setAttribute('width', app.call.sharedVariables.callVideoMinWidth + 'px'); el.setAttribute('height', app.call.sharedVariables.callVideoMinHeight + 'px'); // el.setAttribute('controls', ''); } return config.htmlElement; }, appendVideoToCallDiv() { if (!app.call.sharedVariables.callDivId) { app.sdkParams.consoleLogging && console.log('No Call DIV has been declared!'); return; } let user = config.user, callParentDiv = document.getElementById(app.call.sharedVariables.callDivId), userContainer = document.getElementById("callParticipantWrapper-" + config.userId); if (!userContainer) { callParentDiv.appendChild(config.htmlElements.container); userContainer = document.getElementById("callParticipantWrapper-" + config.userId) } if (user.video) { if (!document.getElementById("callUserVideo-" + config.user.videoTopicName)) { userContainer.appendChild(config.htmlElements[config.user.videoTopicName]); config.htmlElements[config.user.videoTopicName].play(); } else { let targetEl = document.getElementById('callUserVideo-' + config.user.videoTopicName); if(!targetEl.srcObject) { targetEl.srcObject = config.htmlElements[config.user.videoTopicName].srcObject; } } } app.call.currentCall().sendCallDivs() }, videoTopicManager() { // return config.videoTopicManager; }, audioTopicManager() { // return config.audioTopicManager; }, removeAudioWatcherInterval() { if (config.topicMetaData) { clearInterval(config.topicMetaData.audioLevelInterval); } }, replaceVideoStream(newStream, callback) { const deviceManager = app.call.deviceManager; if(deviceManager.mediaStreams.getVideoInput()) { if(app.sdkParams.enableCallDivs) { config.htmlElements[config.user.videoTopicName].srcObject = newStream; } try{ app.call.currentCall().sendPeerManager().changeVideoSendStream(newStream); let targetEl = document.getElementById('callUserVideo-' + config.user.videoTopicName); if(app.sdkParams.enableCallDivs && !targetEl.srcObject) { targetEl.srcObject = config.htmlElements[config.user.videoTopicName].srcObject; } callback && callback(); } catch (error) { app.call.currentCall().raiseCallError({code: errorList.CHANGE_MEDIA_DEVICE_FAILED.code, message: error.message}, null, true) publicized.stopVideo(); setTimeout(function () { app.call.currentCall().sendCallDivs(); }) } } }, changeVideoStream(deviceId, callback) { const conf = {deviceId: {exact: deviceId}}, deviceManager = app.call.deviceManager; if(deviceManager.mediaStreams.getVideoInput()) { deviceManager.mediaStreams.stopVideoInput(); deviceManager.grantUserMediaDevicesPermissions({video: conf}, (result) => { if (!result.hasError) { const stream = deviceManager.mediaStreams.getVideoInput() if(app.sdkParams.enableCallDivs) { config.htmlElements[config.user.videoTopicName].srcObject = stream; } try{ app.call.currentCall().sendPeerManager().changeVideoSendStream(stream); let targetEl = document.getElementById('callUserVideo-' + config.user.videoTopicName); if(app.sdkParams.enableCallDivs && !targetEl.srcObject) { targetEl.srcObject = config.htmlElements[config.user.videoTopicName].srcObject; } config.videoStream = stream; callback && callback(); } catch (error) { app.call.currentCall().raiseCallError({code: errorList.CHANGE_MEDIA_DEVICE_FAILED.code, message: error.message}, null, true) publicized.stopVideo(); setTimeout(function () { app.call.currentCall().sendCallDivs(); }) } } else { app.call.currentCall().raiseCallError({code: result.errorCode, message: result.errorMessage}, null, true) publicized.stopVideo(); setTimeout(function () { app.call.currentCall().sendCallDivs(); }) } }); } else { throw new Error('[SDK] Webcam is not active') } }, changeAudioStream(deviceId, callback) { const conf = {deviceId: {exact: deviceId}}, deviceManager = app.call.deviceManager; if(deviceManager.mediaStreams.getAudioInput()) { deviceManager.mediaStreams.stopAudioInput(); app.call.deviceManager .grantUserMediaDevicesPermissions({audio: conf}, (result) => { if (!result.hasError) { try{ const stream = app.call.deviceManager.mediaStreams.getAudioInput() app.call.currentCall().sendPeerManager().getPeer().switchAudioStream(stream); onTrackCallback({ currentTrackId: stream.getTracks()[0].id, clientId: config.user.clientId, topic: config.user.audioTopicName, mediaType: 1, stream: stream, }, stream.getTracks()[0]); config.audioStream = stream; callback && callback(); } catch (error) { app.call.currentCall().raiseCallError({code: errorList.CHANGE_MEDIA_DEVICE_FAILED.code, message: error.message}, null, true) publicized.stopAudio(); } } else { app.call.currentCall().raiseCallError({code: result.errorCode, message: result.errorMessage}, null, true) publicized.stopAudio(); } }) } else { throw new Error('[SDK] Microphone is not active') } }, async startAudio(sendTopic, conf) { if (config.isMe) { app.call.deviceManager.grantUserMediaDevicesPermissions({audio: true},(result)=>{ if(!result.hasError) { config.audioIsOpen = true; config.user.mute = false; // config.audioReceiveVersion = conf.version; addSendTrackToPeerManager('audio') } }) } else { config.audioIsOpen = true; config.user.mute = false; config.audioReceiveVersion = conf.version; addReceiveTrackToPeerManager('audio', conf) } }, async startVideo(sendTopic, conf) { if (config.isMe) { app.call.deviceManager.grantUserMediaDevicesPermissions({video: {width: app.call.sharedVariables.callVideoMinWidth, framerate: 10}}, (result)=>{ if(!result.hasError) { config.user.video = true; config.videoIsOpen = true; addSendTrackToPeerManager('video') } }); } else { config.user.video = true; config.videoIsOpen = true; config.videoReceiveVersion = conf.version; addReceiveTrackToPeerManager('video', conf) } }, pauseVideoSendStream() { let localStream = app.call.deviceManager.mediaStreams.getVideoInput() if (localStream) localStream.getTracks()[0].enabled = false; }, pauseAudioSendStream() { let localStream = app.call.deviceManager.mediaStreams.getAudioInput() if (localStream) localStream.getTracks()[0].enabled = false; }, // pauseSendStream() { // let localStream; // switch (config.mediaType) { // case 'audio': // localStream = app.call.currentCall().deviceManager().mediaStreams.getAudioInput() // break; // case 'video': // if(config.isScreenShare) { // localStream = app.call.currentCall().deviceManager().mediaStreams.getScreenShareInput(); // } else { // localStream = app.call.currentCall().deviceManager().mediaStreams.getVideoInput(); // } // } // if(localStream) // localStream.getTracks()[0].enabled = false; // }, resumeVideoSendStream() { let localStream = app.call.deviceManager.mediaStreams.getVideoInput() if (localStream) localStream.getTracks()[0].enabled = true; }, resumeAudioSendStream() { let localStream = app.call.deviceManager.mediaStreams.getAudioInput() if (localStream) localStream.getTracks()[0].enabled = true; }, startSLowLink() { app.chatEvents.fireEvent('callEvents', { type: 'SLOW_LINK', callId: config.callId, message: `Slow link`, userId: config.userId }); config.slowLinkTimeout && clearTimeout(config.slowLinkTimeout); config.slowLinkTimeout = setTimeout(() => { app.chatEvents.fireEvent('callEvents', { type: 'SLOW_LINK_RESOLVED', callId: config.callId, message: `Slow link resolved`, userId: config.userId }); }, 10000); }, correctWithServerData(remoteInfo) { if(config.videoIsOpen && !remoteInfo.video) { publicized.stopVideo(); } if(!config.videoIsOpen && remoteInfo.video) { config.user.video = true; } if(config.audioIsOpen && remoteInfo.mute) { publicized.stopAudio(); } if(!config.audioIsOpen && !remoteInfo.mute) { config.user.mute = false; } }, getAudioStreamHasChanged() { let hasChange = (config.oldAudioStreamId != config.newAudioStreamId); config.oldAudioStreamId = config.newAudioStreamId; return hasChange; }, getVideoStreamHasChanged() { let hasChange = (config.oldVideoStreamId != config.newVideoStreamId); config.oldVideoStreamId = config.newVideoStreamId; return hasChange; }, async destroy() { await publicized.stopVideo(); await publicized.stopAudio(); config.isDestroyed = true; // await publicized.destroyVideo(); // await publicized.destroyAudio(); config.htmlElements = {}; user = null; }, async stopAudio() { config.user.mute = true; config.audioIsOpen = false; let currentCall = app.call.currentCall(); if(!currentCall.isDestroyed()) { if (config.isMe) currentCall.sendPeerManager() && currentCall.sendPeerManager().removeTrack(config.user.audioTopicName); else { currentCall.receivePeerManager() && currentCall.receivePeerManager().removeTrack(config.user.audioTopicName); } } await publicized.destroyAudio(); }, async destroyAudio() { config.audioReceiveVersion = -1; console.log('[media][audio] destroy, topic: ', config.user.audioTopicName); if (config.audioObject) { config.audioObject.srcObject = null; config.audioObject = null; } config.audioIsOpen = false; if(app.call.currentCall().receivePeerManager()) { app.call.currentCall().receivePeerManager().removeTrack(config.user.audioTopicName) } publicized.removeAudioWatcherInterval(); }, async stopVideo() { config.user.video = false; config.videoIsOpen = false; let currentCall = app.call.currentCall(); if(!currentCall.isDestroyed()) { if (config.isMe) { currentCall.sendPeerManager() && currentCall.sendPeerManager().removeTrack(config.user.videoTopicName) } else { currentCall.receivePeerManager() && currentCall.receivePeerManager().removeTrack(config.user.videoTopicName); } } await publicized.destroyVideo(); }, async destroyVideo() { config.videoReceiveVersion = -1; config.videoIsOpen = false; if (config.htmlElements[config.user.videoTopicName]) { config.htmlElements[config.user.videoTopicName].remove(); delete config.htmlElements[config.user.videoTopicName]; } if(app.call.currentCall().receivePeerManager()) { app.call.currentCall().receivePeerManager().removeTrack(config.user.videoTopicName) } }, getReceiveVideoVersion(){ return config.videoReceiveVersion }, getReceiveAudioVersion(){ return config.audioReceiveVersion }, processTrackChange(conf) { if (conf.topic.indexOf('Vi-') > -1) { if (!config.videoIsOpen && conf.isReceiving) { publicized.startVideo(conf.topic.replace('Vi-', ''), conf); } else if (config.videoIsOpen && !conf.isReceiving) { publicized.stopVideo(); } else if (config.videoIsOpen && conf.isReceiving && config.videoReceiveVersion != conf.version) { publicized.stopVideo(); setTimeout(() => { publicized.startVideo(conf.topic.replace('Vi-', ''), conf); }, 200); } } else if (conf.topic.indexOf('Vo-') > -1) { // config.audioReceiveVersion = conf.version; if (!config.audioIsOpen && conf.isReceiving) { publicized.startAudio(conf.topic.replace('Vo-', ''), conf); } else if (config.audioIsOpen && !conf.isReceiving) { publicized.stopAudio(); } else if (config.audioIsOpen && conf.isReceiving && config.audioReceiveVersion != conf.version) { publicized.stopAudio(); setTimeout(() => { publicized.startAudio(conf.topic.replace('Vo-', ''), conf); }, 200); } } }, isVideoOpen(){ return config.videoIsOpen; }, isAudioOpen(){ return config.audioIsOpen; }, resetTopicVersion(topic) { if (topic.mediaType == 0) { config.videoReceiveVersion = -1; } else if (topic.mediaType == 1) { config.audioReceiveVersion = -1; } }, watchAudioLevel: function (stream) { let user = config.user, topicMetadata = config.topicMetaData; // Create and configure the audio pipeline const analyzer = app.call.audioCtx().createAnalyser(); analyzer.fftSize = 512; analyzer.smoothingTimeConstant = 0.1; const sourceNode = app.call.audioCtx().createMediaStreamSource(stream); sourceNode.connect(analyzer); // Analyze the sound topicMetadata.audioLevelInterval = setInterval(() => { // Compute the max volume level (-Infinity...0) const fftBins = new Float32Array(analyzer.frequencyBinCount); // Number of values manipulated for each sample analyzer.getFloatFrequencyData(fftBins); // audioPeakDB varies from -Infinity up to 0 const audioPeakDB = Math.max(...fftBins); // Compute a wave (0...) const frequencyRangeData = new Uint8Array(analyzer.frequencyBinCount); analyzer.getByteFrequencyData(frequencyRangeData); const sum = frequencyRangeData.reduce((p, c) => p + c, 0); // audioMeter varies from 0 to 10 const audioMeter = Math.sqrt(sum / frequencyRangeData.length); //console.log({audioMeter}, {audioPeakDB}); if (audioPeakDB > -50 && audioMeter > 0) { app.chatEvents.fireEvent('callStreamEvents', { type: 'USER_SPEAKING', userId: config.userId, audioLevel: convertToAudioLevel(audioPeakDB), isNoise: false, isMute: false }); } else if (audioPeakDB !== -Infinity && audioPeakDB < -60 && audioMeter > 0) { app.chatEvents.fireEvent('callStreamEvents', { type: 'USER_SPEAKING', userId: config.userId, audioLevel: 0, isNoise: true, isMute: false }); } else if (audioPeakDB === -Infinity && audioMeter == 0) { app.chatEvents.fireEvent('callStreamEvents', { type: 'USER_SPEAKING', userId: config.userId, audioLevel: 0, isNoise: false, isMute: true }); } }, 500); function convertToAudioLevel(soundPower) { if (soundPower <= -60) { return 0; } else if (soundPower >= -60 && soundPower < -50) { return 1; } else if (soundPower >= -50 && soundPower < -40) { return 2; } else if (soundPower >= -40 && soundPower < 30) { return 3; } else if (soundPower >= -30) { return 4; } } }, } function addSendTrackToPeerManager(media, retries) { if(retries > 3) return; if(!app.call.currentCall().sendPeerManager()) { retryAgain(media, retries + 1); return; } let addRes; if(media == 'video') { const stream = app.call.deviceManager.mediaStreams.getVideoInput() addRes = app.call.currentCall().sendPeerManager().addTrack({ currentTrackId: stream.getTracks()[0].id, clientId: config.user.clientId, topic: config.user.videoTopicName, mediaType: 0, stream: stream, onTrackCallback, onOpenFailure, }); } else { addRes = app.call.currentCall().sendPeerManager().addTrack({ clientId: config.user.clientId, topic: config.user.audioTopicName, mediaType: 1, stream: app.call.deviceManager.mediaStreams.getAudioInput(), onTrackCallback, onOpenFailure, }); } if(addRes === 0) { retryAgain(media, retries + 1); } function retryAgain(media, retries){ if(config.isDestroyed) return; setTimeout(()=>{ addSendTrackToPeerManager(media, retries); }, 100); } } function addReceiveTrackToPeerManager(media, conf, retries = 0){ if(retries > 3) return; if(!app.call.currentCall().receivePeerManager()) { retryAgain(media, conf, retries + 1); return; } let addRes; if(media == 'video') { addRes = app.call.currentCall().receivePeerManager().addTrack({ clientId: config.user.clientId, topic: config.user.videoTopicName, mediaType: 0, requestedTopicVersion: conf.version, mline: (conf && conf.mline), onTrackCallback, onOpenFailure, onUpdateSuccess }) } else { addRes = app.call.currentCall().receivePeerManager().addTrack({ clientId: config.user.clientId, topic: config.user.audioTopicName, mediaType: 1, requestedTopicVersion: conf.version, mline: (conf && conf.mline), onTrackCallback, onOpenFailure, onUpdateSuccess }); } if(addRes === 0) { retryAgain(media, conf, retries + 1); } function retryAgain(media, conf, retries){ if(config.isDestroyed) return; setTimeout(()=>{ addReceiveTrackToPeerManager(media, conf, retries); }, 100); } } function onOpenFailure(item) { if (item.mediaType == 0) { config.videoIsOpen = false; } else if (item.mediaType == 1) { config.audioIsOpen = false; } if(config.isMe) { if(item.mediaType == 0) { publicized.startVideo(config.user.topicSend, null); } else { publicized.startAudio(config.user.topicSend, null); } } // app.call.currentCall().sendCallMessage({ // id: 'REQUEST_RECEIVING_MEDIA', // token: app.sdkParams.token, // chatId: config.callId, // brokerAddress: config.user.brokerAddress, // }, null, {}); } function onUpdateSuccess(line) { if (line.mediaType == 0) { config.videoReceiveVersion = line.requestedTopicVersion; } else if (line.mediaType == 1) { config.audioReceiveVersion = line.requestedTopicVersion; } } function onTrackCallback(line, track) { let stream, trc, isAudio = (line.topic.indexOf('Vo-') > -1), isVideo = (line.topic.indexOf('Vi-') > -1); if(isVideo) { if(track) trc = track; // stream = new MediaStream([track]); else trc = config.streamTracks.video // stream = new MediaStream([config.streamTracks.video]); config.streamTracks.video = trc; } else if(isAudio) { if(track) trc = track; else trc = config.streamTracks.audio config.streamTracks.audio = trc; } stream = new MediaStream([trc]); if(isAudio){ config.audioStream = stream; if(!config.newAudioStreamId) { config.oldAudioStreamId = null; config.newAudioStreamId = config.audioStream.id; } else { config.oldAudioStreamId = config.newAudioStreamId; config.newAudioStreamId = config.audioStream.id; } } else { config.videoStream = stream; if(!config.newVideoStreamId) { config.oldVideoStreamId = null; config.newVideoStreamId = config.videoStream.id; } else { config.oldVideoStreamId = config.newVideoStreamId; config.newVideoStreamId = config.videoStream.id; } } if (config.isMe) { if (isAudio) { publicized.watchAudioLevel(stream); app.call.currentCall().sendCallDivs(); } else if(app.sdkParams.enableCallDivs) { let el = publicized.getVideoHtmlElement(); el.srcObject = stream; config.htmlElements[config.user.videoTopicName] = el; publicized.appendVideoToCallDiv(); } else { //to send streams app.call.currentCall().sendCallDivs(); } } else { if (isAudio) { console.log('[media][audio] create, topic: ', line.topic); config.audioObject = new Audio(); config.audioObject.srcObject = stream; // config.audioObject.srcObject = stream; config.audioObject.autoplay = true; config.audioObject.play(); publicized.watchAudioLevel(stream); app.call.currentCall().sendCallDivs(); } else if (isVideo && app.sdkParams.enableCallDivs) { let el = publicized.getVideoHtmlElement(); el.srcObject = null; setTimeout(()=>{ el.srcObject = stream; config.htmlElements[config.user.videoTopicName] = el; publicized.appendVideoToCallDiv(); }, 100); } else { //to send streams app.call.currentCall().sendCallDivs(); } } } function setup(participant) { config.user = participant; if (config.isMe) { config.user.direction = 'send'; } else { config.user.direction = 'receive'; } config.user.videoTopicName = 'Vi-' + config.user.topicSend; config.user.audioTopicName = 'Vo-' + config.user.topicSend; if(app.sdkParams.enableCallDivs) { generateContainerElement(); } } function generateContainerElement() { if (!config.htmlElements.container) { config.htmlElements.container = document.createElement('div'); let el = config.htmlElements.container; el.setAttribute('id', 'callParticipantWrapper-' + config.userId); el.classList.add('participant'); el.classList.add('wrapper'); el.classList.add('user-' + config.userId); el.classList.add((config.isMe ? 'local' : 'remote')); } return config.htmlElements; } setup(user); return publicized; } function CallScreenShare(app, user) { const config = { callId: user.callId, userId: user.userId, isMe: false, user, videoIsOpen: false, type: "screenShare", containerTag: null, htmlElements: {}, videoStream: null, isDestroyed: false, videoReceiveVersion: -1, streamTracks: { video: null, }, oldAudioStreamId: null, newAudioStreamId: null }; const publicized = { getReceiveVideoVersion(){ return config.videoReceiveVersion }, isVideoOpen(){ return config.videoIsOpen; }, getVideoStream() { return config.videoStream }, getAudioTrack() { return null; }, isMe() { return app.call.currentCall().screenShareInfo.iAmOwner(); }, audioIsOpen(){ return false; }, resetTopicVersions() { config.videoReceiveVersion = -1 }, isScreenShare() { return true; }, getVideoStreamHasChanged() { let hasChange = (config.oldVideoStreamId != config.newVideoStreamId); config.oldVideoStreamId = config.newVideoStreamId; if(hasChange && config.htmlElement) config.htmlElement.setAttribute("data-uniqueId", Utility.generateUUID()) return hasChange; }, user() { return config.user; }, setVideoIsOpen(value) { config.videoIsOpen = value; }, setAudioIsOpen(value) { config.audioIsOpen = value; }, getHTMLElements() { return config.htmlElements; }, appendVideoToCallDiv() { if (!app.call.sharedVariables.callDivId) { app.sdkParams.consoleLogging && console.log('No Call DIV has been declared!'); return; } let user = config.user, callParentDiv = document.getElementById(app.call.sharedVariables.callDivId), userContainer = document.getElementById("callParticipantWrapper-" + config.userId); if (!userContainer) { callParentDiv.appendChild(config.htmlElements.container); userContainer = document.getElementById("callParticipantWrapper-" + config.userId); } if (user.video) { if (!document.getElementById("callUserVideo-" + config.user.videoTopicName)) { userContainer.appendChild(config.htmlElements[config.user.videoTopicName]); config.videoStream.getTracks()[0].enabled = true; setTimeout(() => { let el = document.getElementById("callUserVideo-" + config.user.videoTopicName); if (!el) return; el.addEventListener('loadedmetadata', playTheTag); el.srcObject = config.videoStream; function playTheTag() { el.play(); } }, 500); // config.htmlElements[config.user.videoTopicName].srcObject = config.videoStream // config.htmlElements[config.user.videoTopicName].play(); } } app.call.currentCall().sendCallDivs(); }, audioStopManager() { return config.user.audioStopManager }, startAudio(sendTopic) { return; }, startVideo(sendTopic, conf) { config.user.video = true; config.videoIsOpen = true; let iAmOwner = app.call.currentCall().screenShareInfo.iAmOwner(); if (iAmOwner) { app.call.deviceManager.grantScreenSharePermission({closeStream: false}).then(stream => { if (!stream) { alert("Error: could not find screenShareInput"); } else { stream.getVideoTracks()[0].addEventListener("ended", onScreenShareEndCallback); function onScreenShareEndCallback(event) { // Click on browser UI stop sharing button if (!config.user) return; stream.getVideoTracks()[0].removeEventListener("ended", onScreenShareEndCallback); if (app.call.currentCall() && app.call.currentCall().screenShareInfo.isStarted()) { app.call.endScreenShare({ callId: config.callId }); } } addSendTrackToPeerManager() } }).catch(error => { // reject(error) }) } else { config.videoReceiveVersion = conf.version; addReceiveTrackToPeerManager(conf) } }, resumeVideoSendStream() { let localStream = app.call.deviceManager.mediaStreams.getVideoInput() if (localStream) localStream.getTracks()[0].enabled = true; }, processTrackChange(conf) { if ((conf.topic.indexOf('Vi-') > -1 || conf.topic.indexOf('screen') > -1)) { // config.videoReceiveVersion = conf.version; if (!config.videoIsOpen && conf.isReceiving) { publicized.startVideo(conf.topic.replace('Vi-', ''), conf); } else if (config.videoIsOpen && !conf.isReceiving) { config.videoIsOpen = false; publicized.stopVideo(); } else if (config.videoIsOpen && conf.isReceiving && config.videoReceiveVersion != conf.version) {//&& config.videoReceiveVersion != conf.version publicized.stopVideo(); setTimeout(() => { publicized.startVideo(conf.topic.replace('Vi-', ''), conf); }, 200); } } }, resetTopicVersion(topic) { if (topic.mediaType == 2) { config.videoReceiveVersion = -1; } }, async reconnectTopic(media) { await publicized.destroyVideo() await publicized.startVideo(config.user.topic) }, async destroy() { // user.topicMetaData = {}; await publicized.stopVideo(); config.isDestroyed = true; config.htmlElements = {}; config.user = null; }, async stopVideo(notifyServer = true) { config.user.video = false; config.videoIsOpen = false; let iAmOwner = app.call.currentCall().screenShareInfo?.iAmOwner(); let currentCall = app.call.currentCall(); if(!currentCall.isDestroyed()) { // if(notifyServer) { if (iAmOwner) app.call.currentCall().sendPeerManager() && app.call.currentCall().sendPeerManager().removeTrack(config.user.videoTopicName) else app.call.currentCall().receivePeerManager() && app.call.currentCall().receivePeerManager().removeTrack(config.user.videoTopicName); // throw new Error(); // } } await publicized.destroyVideo(); }, destroyAudio() { }, async destroyVideo() { config.videoReceiveVersion = -1; config.videoIsOpen = false; if(app.call.currentCall().receivePeerManager()) { app.call.currentCall().receivePeerManager().removeTrack(config.user.videoTopicName) } if(app.call.currentCall().sendPeerManager()) { app.call.currentCall().sendPeerManager().removeTrack(config.user.videoTopicName) } let el = document.getElementById("callUserVideo-" + config.user.videoTopicName) if (el) { el.remove(); delete config.htmlElements[config.user.videoTopicName]; } }, isOwnerChanged(clientId) { return config.user.clientId != clientId; } } function setup(user) { let iAmOwner = app.call.currentCall().screenShareInfo.iAmOwner(); let obj = { video: true, callId: user.callId, userId: user.userId, topic: user.topicSend, clientId: user.clientId }; obj.direction = iAmOwner ? 'send' : 'receive'; obj.videoTopicName = obj.topic;//`Vi-send-${obj.callId}-screenShare`;//config.topic; config.user = obj; config.isMe = app.call.currentCall().screenShareInfo.iAmOwner(); // publicized.appendUserToCallDiv(generateContainerElement()) if(app.sdkParams.enableCallDivs) { generateContainerElement(); } if (config.user.video && app.call.currentCall().screenShareInfo.iAmOwner()) { publicized.startVideo(obj.topic); } } function addSendTrackToPeerManager(retries) { if(retries > 3) return; if(!app.call.currentCall().sendPeerManager()) { retryAgain( retries + 1); return; } let addRes = app.call.currentCall().sendPeerManager().addTrack({ clientId: config.user.clientId, topic: config.user.videoTopicName, mediaType: 2, isScreenShare: true, stream: app.call.deviceManager.mediaStreams.getScreenShareInput(), onTrackCallback, }); if(addRes === 0) { retryAgain( retries + 1); } function retryAgain( retries){ if(config.isDestroyed) return; setTimeout(()=>{ addSendTrackToPeerManager(retries); }, 10); } } function addReceiveTrackToPeerManager(conf, retries = 0){ if(retries > 3) return; if(!app.call.currentCall().receivePeerManager()) { retryAgain(conf, retries + 1); return; } let addRes = app.call.currentCall().receivePeerManager().addTrack({ clientId: config.user.clientId, topic: config.user.videoTopicName, mediaType: 2, isScreenShare: true, requestedTopicVersion: conf.version, mline: (conf && conf.mline), onTrackCallback, onOpenFailure, onUpdateSuccess }) if(addRes === 0) { retryAgain(conf, retries + 1); } function retryAgain(conf, retries){ if(config.isDestroyed) return; setTimeout(()=>{ addReceiveTrackToPeerManager(conf, retries); }, 10); } } function onOpenFailure(item) { if (item.mediaType == 2) { config.videoIsOpen = false; } if(publicized.isMe()) { if(item.mediaType == 2) { publicized.startVideo(config.user.topicSend, null); } } // app.call.currentCall().sendCallMessage({ // id: 'REQUEST_RECEIVING_MEDIA', // token: app.sdkParams.token, // chatId: config.callId, // brokerAddress: config.user.brokerAddress, // }, null, {}); } function generateContainerElement() { if (!config.htmlElements.container) { config.htmlElements.container = document.createElement('div'); let el = config.htmlElements.container; el.setAttribute('id', 'callParticipantWrapper-' + config.userId); el.classList.add('participant'); el.classList.add('wrapper'); el.classList.add('user-' + config.userId); el.classList.add((config.isMe ? 'local' : 'remote')); } return config.htmlElements; } function getVideoHtmlElement() { let elementUniqueId = Utility.generateUUID(); if (config.user.video && !config.htmlElement) { config.htmlElement = document.createElement('video'); let el = config.htmlElement; el.setAttribute('id', 'callUserVideo-' + config.user.videoTopicName); el.setAttribute('class', app.call.sharedVariables.callVideoTagClassName); el.setAttribute('playsinline', ''); el.setAttribute('muted', ''); el.setAttribute('autoplay', ''); el.setAttribute('data-uniqueId', elementUniqueId); el.setAttribute('width', app.call.sharedVariables.callVideoMinWidth + 'px'); el.setAttribute('height', app.call.sharedVariables.callVideoMinHeight + 'px'); // el.setAttribute('controls', ''); } return config.htmlElement; } function onUpdateSuccess(line) { config.videoReceiveVersion = line.requestedTopicVersion; } function onTrackCallback(line, track) { let trc; if(line.mediaType == 2) { if(track) trc = track; // stream = new MediaStream([track]); else trc = config.streamTracks.video } // trc.onunmute = (event) => { // console.log('[debug][onTrackCallback][trc.onunmute] ', event) // } // trc.onmute = (event) => { // console.log('[debug][onTrackCallback][trc.onmute] ', event) // } config.streamTracks.video = trc; let stream = new MediaStream([trc]); config.videoStream = stream; if(!config.newVideoStreamId) { config.oldVideoStreamId = null; config.newVideoStreamId = config.videoStream.id; } else { config.oldVideoStreamId = config.newVideoStreamId; config.newVideoStreamId = config.videoStream.id; } if(app.sdkParams.enableCallDivs) { let el = getVideoHtmlElement(); // el.addEventListener('loadedmetadata', playTheTag); // el.srcObject = stream; config.htmlElements[config.user.videoTopicName] = el; publicized.appendVideoToCallDiv(); } else { app.call.currentCall().sendCallDivs(); } } setup(user); return publicized; } export {CallUser, CallScreenShare}