656 lines
23 KiB
JavaScript
656 lines
23 KiB
JavaScript
"use strict";
|
|
|
|
// todo: deprecate
|
|
const iceConfig = {
|
|
iceServers: [{
|
|
urls: [
|
|
'stun:stun1.l.google.com:19302',
|
|
'stun:stun2.l.google.com:19302',
|
|
],
|
|
}],
|
|
iceCandidatePoolSize: 10,
|
|
};
|
|
|
|
const displayMediaOptions = {
|
|
noConstraint: {
|
|
video: {
|
|
height: 1080,
|
|
frameRate: 120,
|
|
},
|
|
audio: true,
|
|
},
|
|
v720p30: {
|
|
video: {
|
|
height: 1080,
|
|
frameRate: 120,
|
|
},
|
|
audio: true,
|
|
},
|
|
v480p60: {
|
|
video: {
|
|
height: 480,
|
|
frameRate: 60,
|
|
},
|
|
audio: true,
|
|
},
|
|
};
|
|
|
|
// const rtpPeerConnectionOptions = {
|
|
// stunGoogle: {
|
|
// iceServers: [{
|
|
// urls: [
|
|
// 'stun:stun1.l.google.com:19302',
|
|
// 'stun:stun2.l.google.com:19302',
|
|
// ],
|
|
// }],
|
|
// iceCandidatePoolSize: 10,
|
|
// },
|
|
// noStun: {
|
|
// iceServers: [],
|
|
// }
|
|
// };
|
|
|
|
const rtpPeerConnectionOptions = {
|
|
stunGoogle: {
|
|
iceServers: [
|
|
{
|
|
urls: "stun:relay.metered.ca:80",
|
|
},
|
|
{
|
|
urls: "turn:relay.metered.ca:80",
|
|
username: "f0f0d71ba61e44b74c0e4e9e",
|
|
credential: "gKLJ6NUO0ZqsVN1D",
|
|
},
|
|
{
|
|
urls: "turn:relay.metered.ca:443",
|
|
username: "f0f0d71ba61e44b74c0e4e9e",
|
|
credential: "gKLJ6NUO0ZqsVN1D",
|
|
},
|
|
{
|
|
urls: "turn:relay.metered.ca:443?transport=tcp",
|
|
username: "f0f0d71ba61e44b74c0e4e9e",
|
|
credential: "gKLJ6NUO0ZqsVN1D",
|
|
},
|
|
],
|
|
},
|
|
noStun: {
|
|
iceServers: [],
|
|
}
|
|
}
|
|
|
|
const preset = {
|
|
balanced: {
|
|
displayMediaOption: displayMediaOptions.v720p30,
|
|
rtpPeerConnectionOption: rtpPeerConnectionOptions.stunGoogle,
|
|
},
|
|
performance: {
|
|
displayMediaOption: displayMediaOptions.v480p60,
|
|
rtpPeerConnectionOption: rtpPeerConnectionOptions.stunGoogle,
|
|
},
|
|
highQuality: {
|
|
displayMediaOption: displayMediaOptions.noConstraint,
|
|
rtpPeerConnectionOption: rtpPeerConnectionOptions.stunGoogle,
|
|
},
|
|
balancedLanOnly: {
|
|
displayMediaOption: displayMediaOptions.v720p30,
|
|
rtpPeerConnectionOption: rtpPeerConnectionOptions.noStun,
|
|
},
|
|
performanceLanOnly: {
|
|
displayMediaOption: displayMediaOptions.v480p60,
|
|
rtpPeerConnectionOption: rtpPeerConnectionOptions.noStun,
|
|
},
|
|
highQualityLanOnly: {
|
|
displayMediaOption: displayMediaOptions.noConstraint,
|
|
rtpPeerConnectionOption: rtpPeerConnectionOptions.noStun,
|
|
},
|
|
};
|
|
|
|
const LaplaceVar = {
|
|
ui: {},
|
|
};
|
|
|
|
function avg(arr) {
|
|
return (arr.reduce((a, b) => a + b, 0) / arr.length) | 0
|
|
}
|
|
|
|
function print(s) {
|
|
LaplaceVar.ui.output.innerHTML += s + '\n';
|
|
}
|
|
|
|
function getBaseUrl() {
|
|
return `${window.location.protocol}//${window.location.host}`
|
|
}
|
|
|
|
function getStreamUrl() {
|
|
return `${getBaseUrl()}/?stream=1`;
|
|
}
|
|
|
|
function getJoinUrl(roomID,BarrierIP="") {
|
|
if(BarrierIP == ""){
|
|
return `${getBaseUrl()}/?id=${roomID}`;
|
|
}
|
|
return `${getBaseUrl()}/?id=${roomID}&barrierip=${BarrierIP}`;
|
|
}
|
|
|
|
function updateRoomUI() {
|
|
LaplaceVar.ui.panel.style.display = 'none';
|
|
LaplaceVar.ui.videoContainer.style.display = 'block';
|
|
LaplaceVar.ui.streamPageUI.style.display = 'block';
|
|
|
|
if (LaplaceVar.roomID) {
|
|
const joinUrl = getJoinUrl(LaplaceVar.roomID);
|
|
LaplaceVar.ui.qrcodeObj = new QRCode(LaplaceVar.ui.qrcode, {
|
|
text: joinUrl,
|
|
width: 128,
|
|
height: 128,
|
|
});
|
|
LaplaceVar.ui.roomText.innerHTML = '<b>RoomID:</b> #' + LaplaceVar.roomID;
|
|
LaplaceVar.ui.joinLinkText.innerHTML = joinUrl;
|
|
LaplaceVar.ui.joinLinkText.href = joinUrl;
|
|
}
|
|
}
|
|
|
|
function initUI() {
|
|
LaplaceVar.ui.btnStream = document.getElementById('btnStream');
|
|
LaplaceVar.ui.btnStartStream = document.getElementById('btnStartStream');
|
|
LaplaceVar.ui.inputRoomID = document.getElementById('inputRoomID');
|
|
LaplaceVar.ui.inputDisplayMediaOption = document.getElementById('inputDisplayMediaOption');
|
|
LaplaceVar.ui.inputRTPPeerConnectionOption = document.getElementById('inputRTPPeerConnectionOption');
|
|
LaplaceVar.ui.joinLinkText = document.getElementById("join-link");
|
|
LaplaceVar.ui.joinForm = document.getElementById('joinForm');
|
|
LaplaceVar.ui.output = document.getElementById('output');
|
|
LaplaceVar.ui.qrcode = document.getElementById("qrcode");
|
|
LaplaceVar.ui.panel = document.getElementById('panel');
|
|
LaplaceVar.ui.roomText = document.getElementById('room-text');
|
|
LaplaceVar.ui.statusNumConn = document.getElementById('statusNumConn');
|
|
LaplaceVar.ui.statusPeers = document.getElementById('statusPeers');
|
|
LaplaceVar.ui.statusPing = document.getElementById('statusPing');
|
|
LaplaceVar.ui.selectOptionPreset = document.getElementById('inputOptionPreset');
|
|
LaplaceVar.ui.streamPageUI = document.getElementById('stream-page-ui');
|
|
LaplaceVar.ui.streamServePageUI = document.getElementById('stream-serve-page-ui');
|
|
LaplaceVar.ui.video = document.getElementById('mainVideo');
|
|
LaplaceVar.ui.videoContainer = document.getElementById('video-container');
|
|
LaplaceVar.ui.barrierIP = document.getElementById('barrierIP');
|
|
LaplaceVar.ui.barrierhostname = document.getElementById('hostname');
|
|
LaplaceVar.ui.serverUsername = document.getElementById('Username');
|
|
LaplaceVar.ui.serverIP = document.getElementById('IP');
|
|
LaplaceVar.ui.SSHPassword = document.getElementById('SSHPassword')
|
|
LaplaceVar.headless = { result :"0"};
|
|
|
|
//getting server hostname
|
|
getServerIPandHostname()
|
|
|
|
LaplaceVar.ui.joinForm.onsubmit = async e => {
|
|
e.preventDefault();
|
|
LaplaceVar.roomID = LaplaceVar.ui.inputRoomID.value;
|
|
LaplaceVar.barrierIP = LaplaceVar.ui.barrierIP.value;
|
|
// check if the following action is executed
|
|
console.log("here")
|
|
// Check if the ip address for barrier is given
|
|
if(LaplaceVar.barrierIP == "") {
|
|
window.history.pushState('', '', getJoinUrl(LaplaceVar.roomID));
|
|
}
|
|
else {
|
|
window.history.pushState('', '', getJoinUrl(LaplaceVar.roomID,LaplaceVar.barrierIP));
|
|
}
|
|
await doJoin(LaplaceVar.roomID,LaplaceVar.barrierIP);
|
|
};
|
|
|
|
LaplaceVar.ui.btnStream.onclick = async () => {
|
|
window.history.pushState('', '', getStreamUrl());
|
|
await doStream();
|
|
};
|
|
LaplaceVar.ui.btnStartStream.onclick = () => {
|
|
LaplaceVar.ui.streamServePageUI.style.display = 'none';
|
|
const mediaOption = JSON.parse(LaplaceVar.ui.inputDisplayMediaOption.value);
|
|
const pcOption = JSON.parse(LaplaceVar.ui.inputRTPPeerConnectionOption.value);
|
|
return startStream(mediaOption, pcOption);
|
|
};
|
|
|
|
for (const presetName of Object.keys(preset)) {
|
|
const optionElement = document.createElement('option');
|
|
optionElement.appendChild(document.createTextNode(presetName));
|
|
optionElement.value = presetName;
|
|
LaplaceVar.ui.selectOptionPreset.appendChild(optionElement);
|
|
}
|
|
LaplaceVar.ui.selectOptionPreset.onchange = () => {
|
|
const v = LaplaceVar.ui.selectOptionPreset.value;
|
|
if (preset[v] != null) {
|
|
LaplaceVar.ui.inputDisplayMediaOption.value = JSON.stringify(preset[v].displayMediaOption, null, 1);
|
|
LaplaceVar.ui.inputRTPPeerConnectionOption.value = JSON.stringify(preset[v].rtpPeerConnectionOption, null, 1);
|
|
}
|
|
};
|
|
const defaultPresetValue = Object.keys(preset)[0];
|
|
LaplaceVar.ui.inputDisplayMediaOption.value = JSON.stringify(preset[defaultPresetValue].displayMediaOption, null, 1);
|
|
LaplaceVar.ui.inputRTPPeerConnectionOption.value = JSON.stringify(preset[defaultPresetValue].rtpPeerConnectionOption, null, 1);
|
|
|
|
LaplaceVar.headless.onactivate = () => {
|
|
LaplaceVar.ui.streamServePageUI.style.display = 'none';
|
|
const defaultPresetValue = Object.keys(preset)[0];
|
|
const mediaOption = JSON.parse(LaplaceVar.ui.inputDisplayMediaOption.value);
|
|
const pcOption = JSON.parse(LaplaceVar.ui.inputRTPPeerConnectionOption.value);
|
|
return startStream(mediaOption, pcOption);
|
|
}
|
|
|
|
//getting SSHPassword
|
|
getSSHPassword()
|
|
|
|
// Getting query string from the URL
|
|
const queryString = window.location.search;
|
|
|
|
// Getting url parameters from the URL
|
|
const urlParams = new URLSearchParams(queryString);
|
|
|
|
// Running headless mode
|
|
if(urlParams.get('mode') == "headless") {
|
|
// setTimeout(function() {
|
|
LaplaceVar.ui.btnStartStream.click()
|
|
// },2000)
|
|
}
|
|
|
|
LaplaceVar.barrierIP = urlParams.get('barrierip')
|
|
|
|
|
|
print("Logs:");
|
|
print("[+] Page loaded");
|
|
}
|
|
|
|
// Gets host name of the server
|
|
// This is so that user can
|
|
// add the host name to connect
|
|
// to laplace
|
|
function getServerIPandHostname() {
|
|
// Source https://stackoverflow.com/questions/247483/http-get-request-in-javascript
|
|
var xmlHttp = new XMLHttpRequest()
|
|
xmlHttp.open( "GET", getHttpUrl() + "/hostname", false ); // false for synchronous request
|
|
xmlHttp.send(null);
|
|
//LaplaceVar.ui.barrierhostname.innerHTML = xmlHttp.responseText
|
|
LaplaceVar.ui.serverUsername.innerHTML = xmlHttp.responseText
|
|
|
|
// Server IP address
|
|
LaplaceVar.ui.serverIP.innerHTML = window.location.hostname
|
|
//console.log(window.location.href.split('?')[0])
|
|
}
|
|
|
|
function getSSHPassword() {
|
|
// Source https://stackoverflow.com/questions/247483/http-get-request-in-javascript
|
|
var xmlHttp = new XMLHttpRequest()
|
|
xmlHttp.open( "GET", getHttpUrl() + "/SSHPassword", false ); // false for synchronous request
|
|
xmlHttp.send(null);
|
|
|
|
LaplaceVar.ui.SSHPassword.innerHTML = xmlHttp.responseText
|
|
}
|
|
|
|
|
|
function updateStatusUIStream() {
|
|
LaplaceVar.status.peers = Object.keys(LaplaceVar.pcs).map(s => s.split('$')[1]);
|
|
LaplaceVar.status.numConn = LaplaceVar.status.peers.length;
|
|
LaplaceVar.ui.statusPeers.innerHTML = LaplaceVar.status.peers
|
|
.map(s => `${s} (${LaplaceVar.pings[LaplaceVar.roomID + '$' + s]} ms)`).join(', ');
|
|
LaplaceVar.ui.statusNumConn.innerHTML = LaplaceVar.status.numConn;
|
|
}
|
|
|
|
function updateStatusUIJoin() {
|
|
LaplaceVar.ui.statusNumConn.innerHTML = LaplaceVar.status.numConn;
|
|
LaplaceVar.ui.statusPeers.innerHTML = LaplaceVar.status.peers.map(s => LaplaceVar.sessionID.endsWith(s) ? s + ' (you)' : s).join(', ');
|
|
}
|
|
|
|
function getWebsocketUrl() {
|
|
if (window.location.protocol === "https:") {
|
|
return `wss://${window.location.host}`
|
|
} else {
|
|
return `ws://${window.location.host}`
|
|
}
|
|
}
|
|
|
|
function getHttpUrl() {
|
|
//if (window.location.protocol === "https:") {
|
|
return window.location.href.split('?')[0]
|
|
// } else {
|
|
// return `${window.location.href}`
|
|
// }
|
|
}
|
|
|
|
async function newRoom(rID) {
|
|
print("[+] Get room ID: " + rID);
|
|
LaplaceVar.roomID = rID;
|
|
updateRoomUI();
|
|
}
|
|
|
|
async function newSessionStream(sessionID, pcOption) {
|
|
print('[+] New session: ' + sessionID);
|
|
LaplaceVar.pcs[sessionID] = new RTCPeerConnection(pcOption);
|
|
LaplaceVar.pcs[sessionID].onicecandidate = e => {
|
|
print('[+] Debug onicecandidate: ' + JSON.stringify(e.candidate));
|
|
if (!e.candidate) {
|
|
print('[+] Debug onicecandidate: got final candidate!');
|
|
return;
|
|
}
|
|
print('[+] Send addCallerIceCandidate to websocket: ' + JSON.stringify(e.candidate));
|
|
LaplaceVar.socket.send(JSON.stringify({
|
|
Type: "addCallerIceCandidate",
|
|
SessionID: sessionID,
|
|
Value: JSON.stringify(e.candidate),
|
|
}))
|
|
};
|
|
LaplaceVar.pcs[sessionID].oniceconnectionstatechange = () => {
|
|
print('[+] Debug oniceconnectionstatechange ' + LaplaceVar.pcs[sessionID].iceConnectionState);
|
|
if (LaplaceVar.pcs[sessionID].iceConnectionState === 'disconnected') {
|
|
print("[-] Disconnected with a Peer " + sessionID);
|
|
LaplaceVar.pcs[sessionID].close();
|
|
delete LaplaceVar.pcs[sessionID];
|
|
delete LaplaceVar.dataChannels[sessionID];
|
|
delete LaplaceVar.pings[sessionID];
|
|
delete LaplaceVar.pingHistories[sessionID];
|
|
updateStatusUIStream();
|
|
}
|
|
};
|
|
updateStatusUIStream();
|
|
LaplaceVar.dataChannels[sessionID] = LaplaceVar.pcs[sessionID].createDataChannel('ping');
|
|
LaplaceVar.dataChannels[sessionID].addEventListener('open', () => {
|
|
print('[+] Start ping interval: ', sessionID);
|
|
LaplaceVar.pingHistories[sessionID] = [];
|
|
LaplaceVar.pingIntervals[sessionID] = window.setInterval(() => {
|
|
const now = new Date().getTime();
|
|
LaplaceVar.dataChannels[sessionID].send('ping ' + now.toString());
|
|
LaplaceVar.dataChannels[sessionID].send('status ' + JSON.stringify(LaplaceVar.status));
|
|
}, 5000);
|
|
});
|
|
LaplaceVar.dataChannels[sessionID].addEventListener('close', () => {
|
|
print('[+] Clear ping interval: ', sessionID);
|
|
window.clearInterval(LaplaceVar.pingIntervals[sessionID]);
|
|
});
|
|
LaplaceVar.dataChannels[sessionID].addEventListener('message', e => {
|
|
if (e.data.startsWith('ping')) {
|
|
LaplaceVar.dataChannels[sessionID].send('pong' + e.data.slice(4));
|
|
} else if (e.data.startsWith('pong')) {
|
|
const now = new Date().getTime();
|
|
const then = parseInt(e.data.slice(4));
|
|
if (!isNaN(then)) {
|
|
LaplaceVar.pingHistories[sessionID].push(now - then);
|
|
if (LaplaceVar.pingHistories[sessionID].length > 3) {
|
|
LaplaceVar.pingHistories[sessionID].shift()
|
|
}
|
|
LaplaceVar.pings[sessionID] = avg(LaplaceVar.pingHistories[sessionID]);
|
|
updateStatusUIStream();
|
|
}
|
|
}
|
|
});
|
|
|
|
LaplaceVar.mediaStream.getTracks().forEach(track => {
|
|
LaplaceVar.pcs[sessionID].addTrack(track, LaplaceVar.mediaStream);
|
|
});
|
|
|
|
|
|
print('[+] Creating offer');
|
|
const offer = await LaplaceVar.pcs[sessionID].createOffer({
|
|
offerToReceiveAudio: true,
|
|
offerToReceiveVideo: true,
|
|
});
|
|
await LaplaceVar.pcs[sessionID].setLocalDescription(offer);
|
|
|
|
print('[+] Send offer to websocket: ' + JSON.stringify(offer));
|
|
LaplaceVar.socket.send(JSON.stringify({
|
|
Type: "gotOffer",
|
|
SessionID: sessionID,
|
|
Value: JSON.stringify(offer),
|
|
}));
|
|
}
|
|
|
|
async function addCalleeIceCandidate(sessionID, v) {
|
|
print('[+] Debug addCalleeIceCandidate ' + sessionID + ' ' + JSON.stringify(v));
|
|
return LaplaceVar.pcs[sessionID].addIceCandidate(v);
|
|
}
|
|
|
|
async function gotAnswer(sessionID, v) {
|
|
print('[+] Debug gotAnswer ' + sessionID + ' ' + JSON.stringify(v));
|
|
return LaplaceVar.pcs[sessionID].setRemoteDescription(new RTCSessionDescription(v));
|
|
}
|
|
|
|
async function doStream() {
|
|
LaplaceVar.ui.panel.style.display = 'none';
|
|
LaplaceVar.ui.streamServePageUI.style.display = 'block';
|
|
}
|
|
|
|
|
|
async function startStream(displayMediaOption, pcOption) {
|
|
LaplaceVar.pcs = {}; // contains RTCPeerConnections
|
|
LaplaceVar.dataChannels = {};
|
|
LaplaceVar.pings = {};
|
|
LaplaceVar.pingHistories = {};
|
|
LaplaceVar.pingIntervals = {};
|
|
LaplaceVar.status = {
|
|
numConn: 0,
|
|
peers: [],
|
|
};
|
|
|
|
updateRoomUI();
|
|
|
|
print('[+] Initiate media: capture display media');
|
|
try {
|
|
// noinspection JSUnresolvedFunction
|
|
LaplaceVar.mediaStream = await navigator.mediaDevices.getDisplayMedia(displayMediaOption);
|
|
} catch(err) {
|
|
alert(err);
|
|
leaveRoom()
|
|
}
|
|
LaplaceVar.ui.video.srcObject = LaplaceVar.mediaStream;
|
|
LaplaceVar.ui.video.muted = true; // prevent duplicate sound played
|
|
|
|
print('[+] Initiate websocket');
|
|
LaplaceVar.socket = new WebSocket(getWebsocketUrl() + '/ws_serve');
|
|
LaplaceVar.socket.onerror = () => {
|
|
alert('WebSocket error');
|
|
leaveRoom();
|
|
};
|
|
LaplaceVar.socket.onopen = async function () {
|
|
print("[+] Connected to websocket");
|
|
};
|
|
LaplaceVar.socket.onmessage = async function (e) {
|
|
try {
|
|
const jsonData = JSON.parse(e.data);
|
|
if (jsonData.Type !== 'beat') {
|
|
print("[+] Received websocket message: " + JSON.stringify(e.data));
|
|
}
|
|
if (jsonData.Type === "newRoom") {
|
|
await newRoom(jsonData.Value);
|
|
} else if (jsonData.Type === "newSession") {
|
|
await newSessionStream(jsonData.SessionID, pcOption);
|
|
} else if (jsonData.Type === "addCalleeIceCandidate") {
|
|
await addCalleeIceCandidate(jsonData.SessionID, JSON.parse(jsonData.Value));
|
|
} else if (jsonData.Type === "gotAnswer") {
|
|
await gotAnswer(jsonData.SessionID, JSON.parse(jsonData.Value));
|
|
}
|
|
} catch (e) {
|
|
print("[!] ERROR: " + e);
|
|
console.error(e);
|
|
}
|
|
};
|
|
}
|
|
|
|
async function newSessionJoin(sID) {
|
|
print('[+] New session: ' + sID);
|
|
LaplaceVar.sessionID = sID;
|
|
LaplaceVar.pc = new RTCPeerConnection(iceConfig);
|
|
LaplaceVar.pc.onicecandidate = e => {
|
|
print('[+] Debug onicecandidate: ' + JSON.stringify(e.candidate));
|
|
if (!e.candidate) {
|
|
print('[+] Debug onicecandidate: got final candidate!');
|
|
return;
|
|
}
|
|
print('[+] Send addCalleeIceCandidate to websocket: ' + JSON.stringify(e.candidate));
|
|
LaplaceVar.socket.send(JSON.stringify({
|
|
Type: "addCalleeIceCandidate",
|
|
SessionID: LaplaceVar.sessionID,
|
|
Value: JSON.stringify(e.candidate),
|
|
}))
|
|
};
|
|
LaplaceVar.pc.oniceconnectionstatechange = () => {
|
|
print('[+] pc.oniceconnectionstatechange ' + LaplaceVar.pc.iceConnectionState);
|
|
if (LaplaceVar.pc.iceConnectionState === 'disconnected') {
|
|
print("[-] Disconnected with Peer");
|
|
LaplaceVar.pc.close();
|
|
LaplaceVar.pc = null;
|
|
}
|
|
};
|
|
LaplaceVar.pc.ontrack = event => {
|
|
// print('[+] Debug pc.ontrack: ' + JSON.stringify(event.track));
|
|
// /* does not work on safari */
|
|
// event.streams[0].getTracks().forEach(track => {
|
|
// print('[+] Debug addTrack ' + JSON.stringify(track));
|
|
// LaplaceVar.mediaStream.addTrack(track)
|
|
// });
|
|
LaplaceVar.mediaStream.addTrack(event.track);
|
|
LaplaceVar.ui.video.srcObject = LaplaceVar.mediaStream;
|
|
try {
|
|
LaplaceVar.ui.video.play();
|
|
} catch {}
|
|
};
|
|
LaplaceVar.pc.addEventListener('datachannel', e => {
|
|
LaplaceVar.dataChannel = e.channel;
|
|
LaplaceVar.dataChannel.addEventListener('open', () => {
|
|
print('[+] Start ping interval');
|
|
LaplaceVar.pingHistory = [];
|
|
LaplaceVar.pingInterval = window.setInterval(() => {
|
|
const now = new Date().getTime();
|
|
LaplaceVar.dataChannel.send('ping ' + now.toString());
|
|
}, 1000);
|
|
});
|
|
LaplaceVar.dataChannel.addEventListener('close', () => {
|
|
print('[+] Clear ping interval');
|
|
clearInterval(LaplaceVar.pingInterval);
|
|
});
|
|
LaplaceVar.dataChannel.addEventListener('message', e => {
|
|
if (e.data.startsWith('ping')) {
|
|
LaplaceVar.dataChannel.send('pong' + e.data.slice(4));
|
|
} else if (e.data.startsWith('pong')) {
|
|
const now = new Date().getTime();
|
|
const then = parseInt(e.data.slice(4));
|
|
if (!isNaN(then)) {
|
|
LaplaceVar.pingHistory.push(now - then);
|
|
if (LaplaceVar.pingHistory.length > 3) {
|
|
LaplaceVar.pingHistory.shift()
|
|
}
|
|
LaplaceVar.ping = avg(LaplaceVar.pingHistory);
|
|
LaplaceVar.ui.statusPing.innerHTML = LaplaceVar.ping + ' ms';
|
|
}
|
|
} else if (e.data.startsWith('status')) {
|
|
LaplaceVar.status = JSON.parse(e.data.slice(7));
|
|
updateStatusUIJoin();
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
async function addCallerIceCandidate(sID, v) {
|
|
print('[+] Debug addCallerIceCandidate ' + sID + ' ' + JSON.stringify(v));
|
|
if (LaplaceVar.sessionID !== sID) return;
|
|
return LaplaceVar.pc.addIceCandidate(v);
|
|
}
|
|
|
|
async function gotOffer(sID, v) {
|
|
print('[+] Debug gotOffer ' + sID + ' ' + JSON.stringify(v));
|
|
if (LaplaceVar.sessionID !== sID) return;
|
|
await LaplaceVar.pc.setRemoteDescription(new RTCSessionDescription(v));
|
|
|
|
print('[+] Create answer');
|
|
const answer = await LaplaceVar.pc.createAnswer();
|
|
await LaplaceVar.pc.setLocalDescription(answer);
|
|
|
|
print('[+] Send answer to websocket: ' + JSON.stringify(answer));
|
|
LaplaceVar.socket.send(JSON.stringify({
|
|
Type: "gotAnswer",
|
|
SessionID: LaplaceVar.sessionID,
|
|
Value: JSON.stringify(answer),
|
|
}))
|
|
}
|
|
|
|
async function doJoin(roomID,BarrierIP = "") {
|
|
if (roomID) {
|
|
LaplaceVar.roomID = roomID;
|
|
} else {
|
|
return alert('roomID is not provided');
|
|
}
|
|
// normalize roomID starting with #
|
|
LaplaceVar.roomID = LaplaceVar.roomID.startsWith('#') ? LaplaceVar.roomID.slice(1) : LaplaceVar.roomID;
|
|
LaplaceVar.status = {
|
|
numConn: 0,
|
|
peers: [],
|
|
};
|
|
|
|
updateRoomUI();
|
|
|
|
print('[+] Initiate media: set remote source');
|
|
LaplaceVar.mediaStream = new MediaStream();
|
|
LaplaceVar.ui.video.srcObject = LaplaceVar.mediaStream;
|
|
|
|
print('[+] Initiate websocket');
|
|
if (BarrierIP == "") {
|
|
LaplaceVar.socket = new WebSocket(getWebsocketUrl() + "/ws_connect?id=" + LaplaceVar.roomID);
|
|
} else {
|
|
LaplaceVar.socket = new WebSocket(getWebsocketUrl() + "/ws_connect?id=" + LaplaceVar.roomID + "&barrierip="+BarrierIP);
|
|
}
|
|
|
|
LaplaceVar.socket.onerror = () => {
|
|
alert('WebSocket error');
|
|
leaveRoom();
|
|
};
|
|
LaplaceVar.socket.onopen = async function () {
|
|
print("[+] Connected to websocket");
|
|
};
|
|
LaplaceVar.socket.onmessage = async function (e) {
|
|
try {
|
|
const jsonData = JSON.parse(e.data);
|
|
if (jsonData.Type !== 'beat') {
|
|
print("[+] Received websocket message: " + JSON.stringify(e.data));
|
|
}
|
|
if (jsonData.Type === "newSession") {
|
|
await newSessionJoin(jsonData.SessionID);
|
|
} else if (jsonData.Type === "addCallerIceCandidate") {
|
|
await addCallerIceCandidate(jsonData.SessionID, JSON.parse(jsonData.Value));
|
|
} else if (jsonData.Type === "gotOffer") {
|
|
await gotOffer(jsonData.SessionID, JSON.parse(jsonData.Value));
|
|
} else if (jsonData.Type === 'roomNotFound') {
|
|
alert('Room not found');
|
|
leaveRoom();
|
|
} else if (jsonData.Type === 'roomClosed') {
|
|
alert('Room closed');
|
|
}
|
|
} catch (e) {
|
|
print("[!] ERROR: " + e);
|
|
console.error(e)
|
|
}
|
|
};
|
|
}
|
|
|
|
function routeByUrl() {
|
|
const u = new URL(window.location);
|
|
const paramId = u.searchParams.get('id');
|
|
if (paramId && paramId.length > 0) {
|
|
return doJoin(paramId);
|
|
}
|
|
const paramStream = u.searchParams.get('stream');
|
|
if (paramStream && paramStream.length > 0) {
|
|
return doStream();
|
|
}
|
|
// If the room ID is provided
|
|
const roomId = u.searchParams.get('roomID');
|
|
if (roomId && roomId.length > 0) {
|
|
AddRoomID(roomId);
|
|
}
|
|
}
|
|
|
|
// Adds the room information to the ID inputRoomID
|
|
function AddRoomID(roomID) {
|
|
document.getElementById('inputRoomID').value = roomID
|
|
}
|
|
|
|
function leaveRoom() {
|
|
window.location.href = getBaseUrl();
|
|
}
|
|
|
|
initUI();
|
|
routeByUrl();
|
|
|
|
|