init
This commit is contained in:
@@ -0,0 +1,473 @@
|
||||
var MovimVisio = {
|
||||
id: null,
|
||||
|
||||
calling: false,
|
||||
|
||||
pc: null,
|
||||
|
||||
screenshareSid: null,
|
||||
screenshareSFUPc: null,
|
||||
|
||||
states: null,
|
||||
services: [],
|
||||
|
||||
localStream: null,
|
||||
localVideo: null,
|
||||
localAudio: null,
|
||||
screenSharing: null,
|
||||
|
||||
observer: null,
|
||||
|
||||
activeSpeakerIntervalId: null,
|
||||
|
||||
bundleRegex: 'a=group:(\\S+) (.+)',
|
||||
midRegex: 'a=mid:(.+)',
|
||||
|
||||
mouseMovement: null,
|
||||
mouseMovementTimeout: 5000,
|
||||
|
||||
startedAtTimer: null,
|
||||
|
||||
sfu: null,
|
||||
|
||||
load: function () {
|
||||
MovimVisio.localVideo = document.getElementById('local_video');
|
||||
|
||||
// Disable the video toggle by default
|
||||
MovimVisio.localVideo.classList.add('video_off');
|
||||
|
||||
MovimVisio.localVideo.addEventListener('loadeddata', () => {
|
||||
MovimVisio.localVideo.play()
|
||||
});
|
||||
|
||||
MovimVisio.screenSharing = document.getElementById('screen_sharing_video');
|
||||
MovimVisio.localAudio = document.getElementById('local_audio');
|
||||
},
|
||||
|
||||
init: function (fullJid, jid, id, withVideo, isMuji, sfuConference, contactName, contactAvatarUrl) {
|
||||
MovimVisio.id = id;
|
||||
|
||||
// Set a lock for the current browser (in case there's several others opened)
|
||||
localStorage.setItem('callId', id);
|
||||
localStorage.setItem('callJid', jid);
|
||||
|
||||
let visio = document.querySelector('#visio');
|
||||
delete visio.dataset.type;
|
||||
visio.dataset.jid = jid;
|
||||
visio.dataset.muji = isMuji ? 'true' : 'false';
|
||||
|
||||
if (typeof sfuConference == 'string') {
|
||||
visio.dataset.muji = 'true'; // To use the Muji layout... without using Muji
|
||||
visio.dataset.sfu = 'true';
|
||||
visio.dataset.jid = sfuConference;
|
||||
|
||||
Visio_ajaxHttpPrepareInfo(sfuConference, true);
|
||||
MovimVisio.activeSpeakerIntervalId = setInterval(MovimJingles.checkActiveSpeaker, 1000);
|
||||
} else {
|
||||
Visio_ajaxHttpPrepareInfo(jid, isMuji);
|
||||
}
|
||||
|
||||
if (isMuji == true) {
|
||||
MovimVisio.mujiPublish(true);
|
||||
MovimVisio.activeSpeakerIntervalId = setInterval(MovimJingles.checkActiveSpeaker, 1000);
|
||||
} else {
|
||||
MovimJingles.initSession(jid, fullJid, id, contactName, contactAvatarUrl);
|
||||
|
||||
if (MovimVisio.id) {
|
||||
// Called
|
||||
Visio_ajaxProceed(fullJid, MovimVisio.id);
|
||||
} else {
|
||||
// Calling
|
||||
MovimVisio.id = crypto.randomUUID();
|
||||
localStorage.setItem('callId', MovimVisio.id);
|
||||
Visio_ajaxPropose(fullJid, MovimVisio.id, withVideo, (typeof sfuConference == 'string'));
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof sfuConference == 'string') {
|
||||
MovimVisio.sfu = jid;
|
||||
|
||||
let sfuParticipant = visio.querySelector('div.participant[data-jid="' + jid + '"]');
|
||||
if (sfuParticipant) {
|
||||
// Ugly but works for now
|
||||
sfuParticipant.remove();
|
||||
}
|
||||
|
||||
Visio_ajaxUpgradeCurrentCallToSFU(jid, sfuConference);
|
||||
}
|
||||
|
||||
visio.classList.add('movements');
|
||||
|
||||
Notif.setCallStatus(MovimVisio.states.in_call);
|
||||
|
||||
visio.addEventListener('mousemove', e => {
|
||||
clearTimeout(MovimVisio.mouseMovement);
|
||||
visio.classList.add('movements');
|
||||
|
||||
MovimVisio.mouseMovement = setTimeout(() => {
|
||||
visio.classList.remove('movements');
|
||||
}, MovimVisio.mouseMovementTimeout);
|
||||
})
|
||||
|
||||
if (typeof navigator.mediaDevices.getDisplayMedia == 'undefined') {
|
||||
document.querySelector('#screen_sharing').classList.add('hide');
|
||||
}
|
||||
|
||||
if (MovimVisio.startedAtTimer) {
|
||||
clearInterval(MovimVisio.startedAtTimer);
|
||||
}
|
||||
|
||||
setInterval(VisioUtils.updateStartedAtCounters, 1000);
|
||||
|
||||
Dialog_ajaxClear();
|
||||
Notif.snackbarClear();
|
||||
},
|
||||
|
||||
startScreenshareSFU: function () {
|
||||
if (!MovimVisio.screenSharing.srcObject) return;
|
||||
|
||||
const pc = new RTCPeerConnection({
|
||||
iceServers: MovimVisio.services,
|
||||
bundlePolicy: 'max-bundle',
|
||||
iceCandidatePoolSize: 10
|
||||
});
|
||||
|
||||
MovimVisio.screenshareSid = crypto.randomUUID();
|
||||
MovimVisio.screenshareSFUPc = pc;
|
||||
|
||||
MovimVisio.screenSharing.srcObject.getTracks().forEach(track => {
|
||||
pc.addTrack(track, MovimVisio.screenSharing.srcObject);
|
||||
});
|
||||
|
||||
pc.onicecandidate = event => {
|
||||
if (event.candidate && event.candidate.candidate) {
|
||||
Visio_ajaxCandidate(MovimVisio.sfu, MovimVisio.screenshareSid, event.candidate);
|
||||
}
|
||||
};
|
||||
|
||||
pc.createOffer()
|
||||
.then(offer => pc.setLocalDescription(offer))
|
||||
.then(() => Visio_ajaxSessionInitiate(MovimVisio.sfu, pc.localDescription, MovimVisio.screenshareSid, null))
|
||||
.catch(err => MovimUtils.logError(err));
|
||||
},
|
||||
|
||||
stopScreenshareSFU: function () {
|
||||
if (!MovimVisio.screenshareSFUPc) return;
|
||||
|
||||
Visio_ajaxTerminate(MovimVisio.sfu, MovimVisio.screenshareSid, 'success', false);
|
||||
|
||||
MovimVisio.screenshareSFUPc.close();
|
||||
MovimVisio.screenshareSFUPc = null;
|
||||
MovimVisio.screenshareSid = null;
|
||||
},
|
||||
|
||||
onScreenshareAcceptSDP: function (sdp) {
|
||||
if (!MovimVisio.screenshareSFUPc) return;
|
||||
|
||||
MovimVisio.screenshareSFUPc
|
||||
.setRemoteDescription({ 'sdp': sdp + "\n", 'type': 'answer' })
|
||||
.catch(err => MovimUtils.logError(err));
|
||||
},
|
||||
|
||||
onScreenshareCandidate: function (candidate, mid, mlineindex) {
|
||||
if (!MovimVisio.screenshareSFUPc) return;
|
||||
|
||||
MovimVisio.screenshareSFUPc.addIceCandidate(new RTCIceCandidate({
|
||||
'candidate': candidate.split(/\n/).filter(line => line.startsWith('a=candidate')).join('').substring(2),
|
||||
'sdpMid': mid,
|
||||
'sdpMLineIndex': mlineindex
|
||||
})).catch(error => MovimUtils.logError(error));
|
||||
},
|
||||
|
||||
mujiPublish: function (init) {
|
||||
let pc = new RTCPeerConnection({ 'iceServers': MovimVisio.services });
|
||||
|
||||
/**
|
||||
* Ugly heuristic because it is not possible to match the SDP mids with the tranceiver sender track ids...
|
||||
*/
|
||||
let id = 0;
|
||||
|
||||
MovimVisio.localStream.getTracks().forEach(track => {
|
||||
pc.addTrack(track, MovimVisio.localStream);
|
||||
id++;
|
||||
});
|
||||
|
||||
let screenIds = [];
|
||||
|
||||
if (MovimVisio.screenSharing.srcObject) {
|
||||
MovimVisio.screenSharing.srcObject.getTracks().forEach(track => {
|
||||
pc.addTrack(track);
|
||||
screenIds.push(id);
|
||||
id++;
|
||||
});
|
||||
}
|
||||
|
||||
pc.createOffer().then(function (offer) {
|
||||
if (MovimVisio.screenSharing.srcObject) {
|
||||
// XEP-0507: Jingle Content Category
|
||||
newMedias = offer.sdp.split('m=').map(media => {
|
||||
let mid = media.match(MovimVisio.midRegex);
|
||||
if (mid != null) {
|
||||
if (screenIds.includes(parseInt(mid[1]))) {
|
||||
return media + 'a=content:slides' + "\n";
|
||||
}
|
||||
}
|
||||
|
||||
return media;
|
||||
});
|
||||
|
||||
localDescription = newMedias.join('m=');
|
||||
offer.sdp = localDescription;
|
||||
}
|
||||
|
||||
Visio_ajaxMujiPublish(MovimVisio.id, offer, init);
|
||||
pc.close();
|
||||
});
|
||||
},
|
||||
|
||||
setStates: function (states) {
|
||||
MovimVisio.states = states;
|
||||
},
|
||||
|
||||
setServices: function (services) {
|
||||
MovimVisio.services = services;
|
||||
},
|
||||
|
||||
getUserMedia: function (withVideo) {
|
||||
MovimVisio.load();
|
||||
|
||||
let lobby = document.querySelector('#visio_lobby');
|
||||
|
||||
if (lobby) {
|
||||
VisioUtils.disableLobbyCallButton();
|
||||
}
|
||||
|
||||
MovimTpl.loadingPage();
|
||||
|
||||
return navigator.mediaDevices.getUserMedia(VisioUtils.getConstraints(withVideo)).then(stream => {
|
||||
MovimTpl.finishedPage();
|
||||
|
||||
MovimVisio.localStream = stream;
|
||||
|
||||
if (lobby) {
|
||||
lobby.classList.add('configure');
|
||||
} else if (MovimVisio.localAudio.srcObject == null) {
|
||||
Visio_ajaxClear();
|
||||
return;
|
||||
}
|
||||
|
||||
stream.getTracks().forEach(track => {
|
||||
if (lobby) {
|
||||
VisioUtils.enableLobbyCallButton();
|
||||
}
|
||||
|
||||
if (track.kind == 'audio' && MovimVisio.localAudio && MovimVisio.localAudio.srcObject == null /* In case we're just adding the video */) {
|
||||
MovimVisio.localAudio.srcObject = stream;
|
||||
} else if (withVideo && track.kind === 'video') {
|
||||
MovimVisio.localVideo.srcObject = stream;
|
||||
|
||||
// Toggle the video on
|
||||
MovimVisio.localVideo.classList.remove('video_off');
|
||||
document.querySelector('#toggle_video i').innerText = 'videocam';
|
||||
|
||||
if (lobby) {
|
||||
let cameraPreview = lobby.querySelector('video#camera_preview');
|
||||
cameraPreview.addEventListener('loadeddata', () => cameraPreview.play());
|
||||
cameraPreview.srcObject = stream;
|
||||
cameraPreview.disablePictureInPicture = true;
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
VisioUtils.handleAudio();
|
||||
|
||||
if (lobby) {
|
||||
navigator.mediaDevices.enumerateDevices().then(devices => MovimVisio.gotDevices(withVideo, devices));
|
||||
}
|
||||
}, (e) => {
|
||||
MovimTpl.finishedPage();
|
||||
});
|
||||
},
|
||||
|
||||
gotDevices: function (withVideo, devicesInfo) {
|
||||
microphoneFound = false;
|
||||
cameraFound = false;
|
||||
|
||||
let microphoneSelect = document.querySelector('select[name=default_microphone]');
|
||||
microphoneSelect.onchange = (e) => {
|
||||
localStorage.defaultMicrophone = e.target.value;
|
||||
MovimVisio.getUserMedia(withVideo);
|
||||
};
|
||||
microphoneSelect.innerText = '';
|
||||
|
||||
VisioUtils.handleAudio();
|
||||
|
||||
let cameraSelect = document.querySelector('select[name=default_camera]');
|
||||
|
||||
if (cameraSelect) {
|
||||
cameraSelect.addEventListener('change', e => {
|
||||
localStorage.defaultCamera = e.target.value;
|
||||
|
||||
let cameraPreview = document.querySelector('video#camera_preview');
|
||||
|
||||
if (cameraPreview.srcObject) {
|
||||
cameraPreview.srcObject.getTracks().forEach(track => track.stop());
|
||||
}
|
||||
|
||||
cameraPreview.srcObject = null;
|
||||
|
||||
MovimVisio.getUserMedia(withVideo);
|
||||
});
|
||||
cameraSelect.innerText = '';
|
||||
}
|
||||
|
||||
for (const deviceInfo of devicesInfo) {
|
||||
if (deviceInfo.kind === 'audioinput') {
|
||||
const option = document.createElement('option');
|
||||
option.value = deviceInfo.deviceId;
|
||||
option.text = deviceInfo.label || `Microphone ${microphoneSelect.length + 1}`;
|
||||
|
||||
if (deviceInfo.deviceId == localStorage.defaultMicrophone) {
|
||||
option.selected = true;
|
||||
microphoneFound = true;
|
||||
}
|
||||
|
||||
microphoneSelect.appendChild(option);
|
||||
}
|
||||
|
||||
if (withVideo && deviceInfo.kind === 'videoinput') {
|
||||
const option = document.createElement('option');
|
||||
option.value = deviceInfo.deviceId;
|
||||
option.text = deviceInfo.label || `Camera ${microphoneSelect.length + 1}`;
|
||||
|
||||
if (deviceInfo.deviceId == localStorage.defaultCamera) {
|
||||
option.selected = true;
|
||||
cameraFound = true;
|
||||
}
|
||||
|
||||
// Sometimes we can have two devices with the same id
|
||||
if (!cameraSelect.querySelector('option[value="' + deviceInfo.deviceId + '"]')) {
|
||||
cameraSelect.appendChild(option);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (microphoneFound == false) {
|
||||
localStorage.defaultMicrophone = microphoneSelect.value;
|
||||
}
|
||||
|
||||
if (withVideo && cameraFound == false) {
|
||||
localStorage.defaultCamera = cameraSelect.value;
|
||||
}
|
||||
},
|
||||
|
||||
clear: function () {
|
||||
MovimTpl.finishedPage();
|
||||
|
||||
MovimVisio.id = null;
|
||||
MovimVisio.sfu = null;
|
||||
|
||||
Notif.setCallStatus(null);
|
||||
|
||||
clearInterval(MovimVisio.activeSpeakerIntervalId);
|
||||
|
||||
let visio = document.querySelector('#visio');
|
||||
//delete visio.dataset.type;
|
||||
delete visio.dataset.jid;
|
||||
delete visio.dataset.muji;
|
||||
delete visio.dataset.sfu;
|
||||
|
||||
if (document.fullscreenElement) {
|
||||
document.exitFullscreen();
|
||||
}
|
||||
|
||||
VisioUtils.stopAudio();
|
||||
|
||||
if (MovimVisio.localAudio) {
|
||||
let stream = MovimVisio.localAudio.srcObject;
|
||||
|
||||
if (stream) {
|
||||
stream.getTracks().forEach(track => track.stop());
|
||||
stream = null;
|
||||
}
|
||||
|
||||
MovimVisio.localAudio.srcObject = null;
|
||||
}
|
||||
|
||||
if (MovimVisio.localVideo) {
|
||||
let stream = MovimVisio.localVideo.srcObject;
|
||||
|
||||
if (stream) {
|
||||
stream.getTracks().forEach(track => track.stop());
|
||||
stream = null;
|
||||
}
|
||||
|
||||
MovimVisio.localVideo.srcObject = null;
|
||||
}
|
||||
|
||||
if (MovimVisio.localStream) {
|
||||
MovimVisio.localStream.getTracks().forEach(function (track) {
|
||||
track.stop();
|
||||
});
|
||||
MovimVisio.localStream = null;
|
||||
}
|
||||
|
||||
VisioUtils.disableScreenSharing();
|
||||
|
||||
MovimVisio.localAudio = null;
|
||||
MovimVisio.localVideo = null;
|
||||
MovimVisio.screenSharing = null;
|
||||
},
|
||||
|
||||
moveToChat: function (jid) {
|
||||
if (MovimVisio.observer != null) {
|
||||
MovimVisio.observer.disconnect();
|
||||
}
|
||||
|
||||
// SFU case
|
||||
if (MovimVisio.sfu != null) {
|
||||
let visio = document.querySelector('#visio');
|
||||
jid = visio.dataset.jid;
|
||||
}
|
||||
|
||||
var parts = MovimUtils.urlParts();
|
||||
|
||||
if ((parts.page == 'chat' && parts.params[0] == jid)
|
||||
|| (parts.page == 'space' && parts.params[2] == jid)) {
|
||||
const visio = document.getElementById('visio');
|
||||
const body = document.body;
|
||||
|
||||
document.querySelector('#chat_widget header').after(visio);
|
||||
Chat.scrollRestore();
|
||||
|
||||
const callback = (mutationList, observer) => {
|
||||
if (!document.getElementById('visio')) {
|
||||
document.getElementById('endcommon').before(visio);
|
||||
}
|
||||
};
|
||||
|
||||
MovimVisio.observer = new MutationObserver(callback);
|
||||
MovimVisio.observer.observe(body, { childList: true, subtree: true });
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
callStop: function (id, jid) {
|
||||
localStorage.removeItem('callId');
|
||||
localStorage.removeItem('callJid');
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof Visio_ajaxHttpGetStates == 'function') {
|
||||
Visio_ajaxHttpGetStates();
|
||||
}
|
||||
|
||||
if (typeof MovimWebsocket != 'undefined') {
|
||||
MovimWebsocket.attach(() => {
|
||||
if (MovimVisio.services.length == 0 && typeof Visio_ajaxResolveServices == 'function') {
|
||||
Visio_ajaxResolveServices();
|
||||
Visio_ajaxCheckStatus(localStorage.getItem('callId'), localStorage.getItem('callJid'));
|
||||
}
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user