mirror of https://github.com/movim/movim
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
426 lines
14 KiB
426 lines
14 KiB
/*function logError(error) {
|
|
console.log(error.name + ': ' + error.message);
|
|
console.log(error);
|
|
}
|
|
|
|
var Visio = {
|
|
pc: null,
|
|
localVideo: null,
|
|
remoteVideo: null,
|
|
constraints: null,
|
|
audioContext: null,
|
|
max_level_L: 0,
|
|
old_level_L: 0,
|
|
from: null,
|
|
|
|
videoSelect: undefined,
|
|
switchCamera: undefined,
|
|
|
|
setFrom: from => Visio.from = from,
|
|
|
|
gotStream: function() {
|
|
Visio.constraints = window.constraints = {
|
|
audio: true,
|
|
video: {
|
|
deviceId: Visio.videoSelect.value,
|
|
facingMode: 'user',
|
|
width: { ideal: 4096 },
|
|
height: { ideal: 4096 }
|
|
}
|
|
};
|
|
|
|
Visio.switchCamera.classList.add('disabled');
|
|
Visio.localVideo.srcObject = null;
|
|
|
|
// Useful on Android where you can't have both camera enabled at the same time
|
|
var videoTrack = Visio.pc.getSenders().find(rtc => rtc.track && rtc.track.kind == 'video');
|
|
if (videoTrack) videoTrack.track.stop();
|
|
|
|
if(navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
|
|
navigator.mediaDevices.getUserMedia(Visio.constraints)
|
|
.then(function(stream) {
|
|
Visio.switchCamera.classList.remove('disabled');
|
|
|
|
Visio.localVideo.srcObject = stream;
|
|
Visio.handleAudio();
|
|
|
|
// For the first time we attach all the tracks
|
|
if (Visio.pc.getSenders().length == 0) {
|
|
stream.getTracks().forEach(track => Visio.pc.addTrack(track, stream));
|
|
}
|
|
|
|
// Switch camera
|
|
let videoTrack = stream.getVideoTracks()[0];
|
|
var sender = Visio.pc.getSenders().find(s => s.track && s.track.kind == videoTrack.kind);
|
|
|
|
if (sender) {
|
|
sender.replaceTrack(videoTrack);
|
|
}
|
|
|
|
Visio.toggleMainButton();
|
|
|
|
// If we received an offer, we need to answer
|
|
if (Visio.pc.remoteDescription.type == 'offer') {
|
|
Visio.pc.createAnswer(Visio.localDescCreated, logError);
|
|
}
|
|
});
|
|
}
|
|
},
|
|
|
|
gotDevices: function(deviceInfos) {
|
|
Visio.videoSelect.innerText = '';
|
|
|
|
const ids = [];
|
|
|
|
for (let i = 0; i !== deviceInfos.length; ++i) {
|
|
const deviceInfo = deviceInfos[i];
|
|
|
|
if (deviceInfo.kind === 'videoinput' && !ids.includes(deviceInfo.deviceId)) {
|
|
const option = document.createElement('option');
|
|
option.value = deviceInfo.deviceId;
|
|
option.text = deviceInfo.label;
|
|
Visio.videoSelect.add(option);
|
|
ids.push(deviceInfo.deviceId);
|
|
}
|
|
}
|
|
|
|
if (ids.length >= 2) {
|
|
document.querySelector("#visio #switch_camera").classList.add('enabled');
|
|
}
|
|
|
|
Visio.videoSelect.addEventListener('change', e => Visio.gotStream());
|
|
Visio.gotStream();
|
|
},
|
|
|
|
handleAudio: function(stream) {
|
|
Visio.audioContext = new AudioContext();
|
|
|
|
var microphone = Visio.audioContext.createMediaStreamSource(Visio.localVideo.srcObject);
|
|
var javascriptNode = Visio.audioContext.createScriptProcessor(2048, 1, 1);
|
|
|
|
var cnvs = document.querySelector('#visio .level');
|
|
var cnvs_cntxt = cnvs.getContext('2d');
|
|
|
|
microphone.connect(javascriptNode);
|
|
javascriptNode.connect(Visio.audioContext.destination);
|
|
javascriptNode.onaudioprocess = function(event) {
|
|
var inpt_L = event.inputBuffer.getChannelData(0);
|
|
var instant_L = 0.0;
|
|
|
|
var sum_L = 0.0;
|
|
|
|
for(var i = 0; i < inpt_L.length; ++i) {
|
|
sum_L += inpt_L[i] * inpt_L[i];
|
|
}
|
|
|
|
instant_L = Math.sqrt(sum_L / inpt_L.length);
|
|
Visio.max_level_L = Math.max(Visio.max_level_L, instant_L);
|
|
instant_L = Math.max( instant_L, Visio.old_level_L -0.008 );
|
|
Visio.old_level_L = instant_L;
|
|
|
|
cnvs_cntxt.clearRect(0, 0, cnvs.width, cnvs.height);
|
|
cnvs_cntxt.fillStyle = 'white';
|
|
cnvs_cntxt.fillRect(0, 0,(cnvs.width)*(instant_L/Visio.max_level_L),(cnvs.height)); // x,y,w,h
|
|
}
|
|
},
|
|
|
|
onSDP: function(sdp, type) {
|
|
console.log('SDP');
|
|
console.log(type);
|
|
|
|
Visio.pc.setRemoteDescription(
|
|
new RTCSessionDescription({'sdp': sdp + "\n", 'type': type}),
|
|
() => {
|
|
Visio_ajaxGetCandidates();
|
|
},
|
|
(error) => {
|
|
Visio.goodbye('incompatible-parameters');
|
|
logError(error);
|
|
}
|
|
);
|
|
},
|
|
|
|
localDescCreated: function(desc) {
|
|
Visio.pc.setLocalDescription(desc, () => {
|
|
Visio_ajaxAccept(Visio.pc.localDescription, Visio.from);
|
|
Visio.toggleMainButton();
|
|
}, logError);
|
|
},
|
|
|
|
onCandidates: function(candidates) {
|
|
console.log('Canditates');
|
|
candidates.forEach(candidate => Visio.onCandidate(candidate[0], candidate[1], candidate[2]));
|
|
},
|
|
|
|
onCandidate: function(candidate, mid, mlineindex) {
|
|
console.log('candidate');
|
|
console.log(candidate);
|
|
|
|
Visio_ajaxGetCandidates();
|
|
|
|
if (mid == '') mlineindex = 1;
|
|
|
|
if (Visio.pc.remoteDescription == null || candidate.candidate == '') return;
|
|
|
|
candidate = new RTCIceCandidate({
|
|
'candidate': candidate,
|
|
'sdpMid': mid,
|
|
'sdpMLineIndex' : mlineindex
|
|
});
|
|
|
|
Visio.pc.addIceCandidate(candidate, e => {}, logError);
|
|
},
|
|
|
|
onTerminate: () => {
|
|
let localStream = Visio.localVideo.srcObject;
|
|
|
|
if (localStream) {
|
|
localStream.getTracks().forEach(track => track.stop());
|
|
}
|
|
|
|
let remoteStream = Visio.remoteVideo.srcObject;
|
|
|
|
if (remoteStream) {
|
|
remoteStream.getTracks().forEach(track => track.stop());
|
|
}
|
|
|
|
Visio.localVideo.srcObject = null;
|
|
Visio.remoteVideo.srcObject = null;
|
|
Visio.localVideo.classList.add('hide');
|
|
|
|
if (Visio.pc) Visio.pc.close();
|
|
|
|
document.querySelector('p.state').innerText = Visio.states.ended;
|
|
button = document.querySelector('#main');
|
|
|
|
button.className = 'button action color red';
|
|
button.querySelector('i').className = 'material-icons';
|
|
button.querySelector('i').innerText = 'close';
|
|
|
|
button.onclick = () => window.close();
|
|
},
|
|
|
|
init: (sdp, type) => {
|
|
Visio.toggleMainButton();
|
|
|
|
Visio.setFrom(MovimUtils.urlParts().params.join('/'));
|
|
|
|
const servers = ['stun:stun01.sipphone.com',
|
|
'stun:stun.ekiga.net',
|
|
'stun:stun.fwdnet.net',
|
|
'stun:stun.ideasip.com',
|
|
'stun:stun.iptel.org',
|
|
'stun:stun.rixtelecom.se',
|
|
'stun:stun.schlund.de',
|
|
'stun:stun.l.google.com:19302',
|
|
'stun:stun1.l.google.com:19302',
|
|
'stun:stun2.l.google.com:19302',
|
|
'stun:stun3.l.google.com:19302',
|
|
'stun:stun4.l.google.com:19302',
|
|
'stun:stunserver.org',
|
|
'stun:stun.softjoys.com',
|
|
'stun:stun.voiparound.com',
|
|
'stun:stun.voipbuster.com',
|
|
'stun:stun.voipstunt.com',
|
|
'stun:stun.voxgratia.org',
|
|
'stun:stun.xten.com'
|
|
];
|
|
|
|
const shuffled = servers.sort(() => 0.5 - Math.random());
|
|
|
|
var configuration = {
|
|
'iceServers': [
|
|
{urls: shuffled.slice(0, 2)}
|
|
]
|
|
};
|
|
|
|
Visio.pc = new RTCPeerConnection(configuration);
|
|
|
|
Visio.pc.ontrack = (event) => {
|
|
const stream = event.streams[0];
|
|
if (!Visio.remoteVideo.srcObject || Visio.remoteVideo.srcObject.id !== stream.id) {
|
|
Visio.remoteVideo.srcObject = stream;
|
|
Visio.remoteVideo.classList.add('enabled');
|
|
}
|
|
};
|
|
|
|
Visio.pc.onicecandidate = (evt) => {
|
|
Visio.toggleMainButton();
|
|
|
|
if (evt.candidate) {
|
|
Visio_ajaxCandidate(evt.candidate, Visio.from);
|
|
}
|
|
};
|
|
|
|
Visio.pc.oniceconnectionstatechange = () => Visio.toggleMainButton();
|
|
|
|
Visio.pc.onicegatheringstatechange = function (e) {
|
|
// When we didn't receive the WebRTC termination before Jingle
|
|
if (Visio.pc.iceConnectionState == 'disconnected') {
|
|
Visio.onTerminate();
|
|
}
|
|
|
|
Visio.toggleMainButton();
|
|
};
|
|
|
|
Visio.toggleMainButton();
|
|
|
|
if (sdp && type) {
|
|
Visio.onSDP(sdp, type);
|
|
}
|
|
|
|
// Switch camera
|
|
Visio.videoSelect = document.querySelector('#visio select#visio_source');
|
|
navigator.mediaDevices.enumerateDevices().then(devices => Visio.gotDevices(devices));
|
|
|
|
Visio.switchCamera = document.querySelector("#visio #switch_camera");
|
|
Visio.switchCamera.onclick = () => {
|
|
Visio.videoSelect.selectedIndex++;
|
|
|
|
// No empty selection
|
|
if (Visio.videoSelect.selectedIndex == -1) {
|
|
Visio.videoSelect.selectedIndex++;
|
|
}
|
|
|
|
Visio.gotStream();
|
|
};
|
|
},
|
|
|
|
hello: function() {
|
|
Visio.pc.createOffer((desc) => {
|
|
Visio.pc.setLocalDescription(
|
|
desc,
|
|
() => Visio_ajaxInitiate(Visio.pc.localDescription, Visio.from),
|
|
logError
|
|
);
|
|
}, logError);
|
|
},
|
|
|
|
goodbye: (reason) => {
|
|
Visio.onTerminate();
|
|
Visio_ajaxTerminate(Visio.from, reason);
|
|
},
|
|
|
|
toggleMainButton: function() {
|
|
button = document.getElementById('main');
|
|
state = document.querySelector('p.state');
|
|
|
|
i = button.querySelector('i');
|
|
|
|
button.classList.remove('red', 'green', 'gray', 'orange', 'ring', 'blue');
|
|
button.classList.add('disabled');
|
|
|
|
if (Visio.pc) {
|
|
let length = Visio.pc.getSenders().length;
|
|
|
|
if (Visio.pc.iceConnectionState != 'closed'
|
|
&& length > 0) {
|
|
button.classList.remove('disabled');
|
|
}
|
|
|
|
button.onclick = function() {};
|
|
|
|
if (length == 0) {
|
|
button.classList.add('gray');
|
|
i.innerText = 'more_horiz';
|
|
} else if (Visio.pc.iceConnectionState == 'new') {
|
|
if (Visio.pc.iceGatheringState == 'gathering'
|
|
|| Visio.pc.iceGatheringState == 'complete') {
|
|
button.classList.add('orange');
|
|
i.className = 'material-icons ring';
|
|
i.innerText = 'call';
|
|
state.innerText = Visio.states.ringing;
|
|
|
|
button.onclick = function() { Visio.goodbye(); };
|
|
} else {
|
|
button.classList.add('green');
|
|
i.innerText = 'call';
|
|
|
|
button.onclick = function() { Visio.hello(); };
|
|
}
|
|
} else if (Visio.pc.iceConnectionState == 'checking') {
|
|
button.classList.add('blue');
|
|
i.className = 'material-icons disabled';
|
|
i.innerText = 'more_horiz';
|
|
state.innerText = Visio.states.connecting;
|
|
} else if (Visio.pc.iceConnectionState == 'closed') {
|
|
button.classList.add('gray');
|
|
button.classList.remove('disabled');
|
|
i.innerText = 'call_end';
|
|
|
|
button.onclick = function() { Visio.goodbye(); };
|
|
} else if (Visio.pc.iceConnectionState == 'connected'
|
|
|| Visio.pc.iceConnectionState == 'complete'
|
|
|| Visio.pc.iceConnectionState == 'failed') {
|
|
button.classList.add('red');
|
|
i.className = 'material-icons';
|
|
i.innerText = 'call_end';
|
|
|
|
if (Visio.pc.iceConnectionState == 'failed') {
|
|
state.innerText = Visio.states.failed;
|
|
} else {
|
|
state.innerText = Visio.states.in_call;
|
|
}
|
|
|
|
button.onclick = () => Visio.goodbye();
|
|
}
|
|
}
|
|
},
|
|
|
|
toggleFullScreen: function() {
|
|
var button = document.querySelector('#toggle_fullscreen i');
|
|
|
|
if (!document.fullscreenElement) {
|
|
if (document.body.requestFullscreen) {
|
|
document.body.requestFullscreen();
|
|
}
|
|
|
|
button.innerText = 'fullscreen_exit';
|
|
} else {
|
|
if (document.exitFullscreen) {
|
|
document.exitFullscreen();
|
|
}
|
|
|
|
button.innerText = 'fullscreen';
|
|
}
|
|
},
|
|
|
|
toggleAudio: function() {
|
|
var button = document.querySelector('#toggle_audio i');
|
|
var rtc = Visio.pc.getSenders().find(rtc => rtc.track && rtc.track.kind == 'audio');
|
|
|
|
if (rtc && rtc.track.enabled == 1) {
|
|
rtc.track.enabled = 0;
|
|
button.innerText = 'mic_off';
|
|
} else if (rtc) {
|
|
rtc.track.enabled = 1;
|
|
button.innerText = 'mic';
|
|
}
|
|
},
|
|
|
|
toggleVideo: function() {
|
|
var button = document.querySelector('#toggle_video i');
|
|
var rtc = Visio.pc.getSenders().find(rtc => rtc.track && rtc.track.kind == 'video');
|
|
|
|
if (rtc) {
|
|
if (rtc.track.enabled == 1) {
|
|
rtc.track.enabled = 0;
|
|
button.innerText = 'videocam_off';
|
|
} else {
|
|
rtc.track.enabled = 1;
|
|
button.innerText = 'videocam';
|
|
}
|
|
}
|
|
},
|
|
}
|
|
|
|
MovimWebsocket.attach(() => {
|
|
Visio.localVideo = document.getElementById('video');
|
|
Visio.remoteVideo = document.getElementById('remote_video');
|
|
Visio_ajaxAskInit();
|
|
});
|
|
|
|
window.onbeforeunload = () => {
|
|
Visio.goodbye();
|
|
}
|
|
*/
|