podchat-browser
Version:
Javascript SDK to use POD's Chat Service - Browser Only
1,180 lines (1,083 loc) • 46.8 kB
JavaScript
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}