Files
screen-sharing-rdp/webrtc/web/js/app.js
2020-11-05 23:24:19 +08:00

318 lines
7.4 KiB
JavaScript

let peerConnection = null;
let dataChannel = null;
let resolutionMap = {
screenWidth: 0,
screenHeight: 0,
canvasWidth: 0,
canvasHeight: 0,
};
function showError(error) {
const errorNode = document.querySelector("#error");
if (errorNode.firstChild) {
errorNode.removeChild(errorNode.firstChild);
}
errorNode.appendChild(document.createTextNode(error.message || error));
}
function startSession(offer, screen) {
return fetch("/api/session", {
method: "POST",
body: JSON.stringify({
offer,
screen,
}),
headers: {
"Content-Type": "application/json",
},
})
.then((res) => {
return res.json();
})
.then((msg) => {
return msg.answer;
});
}
function createOffer(pc, { audio, video }) {
return new Promise((accept, reject) => {
pc.onicecandidate = (evt) => {
if (!evt.candidate) {
// ICE Gathering finished
const { sdp: offer } = pc.localDescription;
accept(offer);
}
};
pc.createOffer({
offerToReceiveAudio: audio,
offerToReceiveVideo: video,
})
.then((ld) => {
pc.setLocalDescription(ld);
})
.catch(reject);
});
}
function sendDataMessage(command, data) {
if (dataChannel) {
// Send cordinates
dataChannel.send(
JSON.stringify({
command: command,
data: data,
})
);
}
}
function enableMouseEvents(dataChannel) {
// Start sending mouse cordinates on mouse move in canvas
const remoteCanvas = document.getElementById("remote-canvas");
// On Mouse move
remoteCanvas.addEventListener("mousemove", (event) => {
// Get cordinates
const cordinates = scaleCordinatesToOriginalScreen(event);
// Send cordinates
sendDataMessage("mousemove", {
x: cordinates.x,
y: cordinates.y,
});
});
// On Mouse Click
remoteCanvas.addEventListener("mousedown", (event) => {
let button = "left";
switch (event.which) {
case 1:
button = "left";
break;
case 2:
button = "center";
break;
case 3:
button = "right";
break;
default:
button = "left";
}
sendDataMessage("click", {
button,
});
});
// On Mouse Double Click
remoteCanvas.addEventListener("dblclick", (event) => {
let button = "left";
switch (event.which) {
case 1:
button = "left";
break;
case 2:
button = "center";
break;
case 3:
button = "right";
break;
default:
button = "left";
}
sendDataMessage("dblclick", {
button,
});
});
// On Mouse Scroll
remoteCanvas.addEventListener("wheel", (event) => {
const delta = Math.sign(event.deltaY);
const direction = delta > 0 ? "down" : "up";
sendDataMessage("mousescroll", {
direction,
});
});
/** DOCUMENT LEVEL EVENT LISTENERS */
// Read keyboard events
document.addEventListener("keydown", (event) => {
sendDataMessage("keydown", {
keyCode: event.keyCode,
});
});
}
function scaleCordinatesToOriginalScreen(event) {
const remoteCanvas = document.getElementById("remote-canvas");
// Get canvas size
const rect = remoteCanvas.getBoundingClientRect();
// Get mouse cordinates on canvas
const x = (event.clientX - rect.left).toFixed(0);
const y = (event.clientY - rect.top).toFixed(0);
// Calculate screen percentage based on canvas
const xPer = (x / resolutionMap.canvasWidth) * 100;
const yPer = (y / resolutionMap.canvasHeight) * 100;
// Map percentage to original screen
return {
x: ((resolutionMap.screenWidth * xPer) / 100).toFixed(0),
y: ((resolutionMap.screenHeight * yPer) / 100).toFixed(0),
};
}
function startRemoteSession(screen, remoteVideoNode, stream) {
let pc;
return Promise.resolve()
.then(() => {
pc = new RTCPeerConnection({
iceServers: [{ urls: "stun:stun.l.google.com:19302" }],
});
dataChannel = pc.createDataChannel("messages");
dataChannel.onopen = function (event) {
enableMouseEvents(dataChannel);
// Fetch screen size from server
sendDataMessage("screensize", {});
};
dataChannel.onmessage = function (event) {
try {
const message = JSON.parse(event.data);
switch (message.command) {
case "screensize":
resolutionMap.screenHeight = message.data.height;
resolutionMap.screenWidth = message.data.width;
break;
case "mousepose":
console.log(message);
break;
}
} catch (e) {
console.error(e);
}
};
pc.ontrack = (evt) => {
remoteVideoNode.srcObject = evt.streams[0];
remoteVideoNode.play();
};
stream &&
stream.getTracks().forEach((track) => {
pc.addTrack(track, stream);
});
return createOffer(pc, { audio: false, video: true });
})
.then((offer) => {
return startSession(offer, screen);
})
.then((answer) => {
return pc.setRemoteDescription(
new RTCSessionDescription({
sdp: answer,
type: "answer",
})
);
})
.then(() => pc);
}
function resizeCanvas(canvas, video) {
const w = video.offsetWidth;
const h = video.offsetHeight;
canvas.width = w;
canvas.height = h;
resolutionMap.canvasHeight = h;
resolutionMap.canvasWidth = w;
}
function disconnectSession() {
sendDataMessage("terminate", {});
peerConnection.close();
peerConnection = null;
dataChannel = null;
enableStartStop(true);
setStartStopTitle("Connect");
}
const enableStartStop = (enabled) => {
const startStop = document.querySelector("#start-stop");
if (enabled) {
startStop.removeAttribute("disabled");
} else {
startStop.setAttribute("disabled", "");
}
};
const setStartStopTitle = (title) => {
const startStop = document.querySelector("#start-stop");
startStop.removeChild(startStop.firstChild);
startStop.appendChild(document.createTextNode(title));
};
document.addEventListener("DOMContentLoaded", () => {
let selectedScreen = 0;
const remoteVideo = document.querySelector("#remote-video");
const remoteCanvas = document.querySelector("#remote-canvas");
// Disable right click context on canvas
remoteCanvas.oncontextmenu = function (e) {
e.preventDefault();
};
const startStop = document.querySelector("#start-stop");
remoteVideo.onplaying = () => {
setInterval(() => {
resizeCanvas(remoteCanvas, remoteVideo);
}, 1000);
};
startStop.addEventListener("click", () => {
enableStartStop(false);
const userMediaPromise =
adapter.browserDetails.browser === "safari"
? navigator.mediaDevices.getUserMedia({ video: true })
: Promise.resolve(null);
if (!peerConnection) {
userMediaPromise.then((stream) => {
return startRemoteSession(selectedScreen, remoteVideo, stream)
.then((pc) => {
remoteVideo.style.setProperty("visibility", "visible");
peerConnection = pc;
})
.catch(showError)
.then(() => {
enableStartStop(true);
setStartStopTitle("Disconnect");
});
});
} else {
disconnectSession();
remoteVideo.style.setProperty("visibility", "collapse");
}
});
});
window.addEventListener("beforeunload", () => {
if (peerConnection) {
peerConnection.close();
}
});