201 lines
4.6 KiB
JavaScript
201 lines
4.6 KiB
JavaScript
|
const displayMediaOptions = {
|
||
|
video: {
|
||
|
displaySurface: "browser",
|
||
|
},
|
||
|
audio: {
|
||
|
suppressLocalAudioPlayback: false,
|
||
|
},
|
||
|
preferCurrentTab: false,
|
||
|
selfBrowserSurface: "exclude",
|
||
|
systemAudio: "include",
|
||
|
surfaceSwitching: "include",
|
||
|
monitorTypeSurfaces: "include",
|
||
|
};
|
||
|
|
||
|
const peerConnection = new RTCPeerConnection();
|
||
|
const signaling = Arcast.getBroadcastingChannel(
|
||
|
"screen-sharing",
|
||
|
onSignalReceived
|
||
|
);
|
||
|
|
||
|
var secret = window.crypto.randomUUID();
|
||
|
var receivedAnswer = false;
|
||
|
var receivedOffer = false;
|
||
|
var isOffering = false;
|
||
|
|
||
|
peerConnection.onconnectionstatechange = (evt) => {
|
||
|
console.log("Connection state changed", evt);
|
||
|
};
|
||
|
|
||
|
peerConnection.oniceconnectionstatechange = () => {
|
||
|
console.log("ICE state: ", peerConnection.iceConnectionState);
|
||
|
};
|
||
|
|
||
|
peerConnection.onicecandidate = (evt) => {
|
||
|
signaling.send(
|
||
|
JSON.stringify({
|
||
|
type: "icecandidate",
|
||
|
data: {
|
||
|
candidate: evt.candidate,
|
||
|
},
|
||
|
})
|
||
|
);
|
||
|
};
|
||
|
|
||
|
peerConnection.ontrack = function (e) {
|
||
|
var screenplay = document.getElementById("screenplay");
|
||
|
if (!screenplay) {
|
||
|
screenplay = document.createElement("video");
|
||
|
screenplay.id = "screenplay";
|
||
|
document.body.appendChild(screenplay);
|
||
|
}
|
||
|
|
||
|
screenplay.autoplay = true;
|
||
|
screenplay.playsInline = true;
|
||
|
screenplay.srcObject = e.streams[0];
|
||
|
screenplay.muted = true;
|
||
|
|
||
|
e.track.onended = (e) => (screenplay.srcObject = screenplay.srcObject);
|
||
|
};
|
||
|
|
||
|
function main() {
|
||
|
const urlParams = new URLSearchParams(window.location.search);
|
||
|
if (urlParams.has("secret")) {
|
||
|
secret = urlParams.get("secret");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function shareScreen() {
|
||
|
return Arcast.getInfo().then((info) => {
|
||
|
return navigator.mediaDevices
|
||
|
.getDisplayMedia(displayMediaOptions)
|
||
|
.then((captureStream) => {
|
||
|
isOffering = true;
|
||
|
const videoTrack = captureStream.getVideoTracks()[0];
|
||
|
peerConnection.addTrack(videoTrack, captureStream);
|
||
|
|
||
|
peerConnection.createOffer().then((offer) => {
|
||
|
peerConnection.setLocalDescription(offer).then(() => {
|
||
|
const intervalId = setInterval(() => {
|
||
|
if (receivedAnswer) {
|
||
|
clearInterval(intervalId);
|
||
|
return;
|
||
|
}
|
||
|
signaling.send(
|
||
|
JSON.stringify({
|
||
|
type: "offer",
|
||
|
data: {
|
||
|
secret: secret,
|
||
|
offer: offer,
|
||
|
},
|
||
|
})
|
||
|
);
|
||
|
}, 1000);
|
||
|
|
||
|
const url =
|
||
|
"http://127.0.0.1:" +
|
||
|
info.port +
|
||
|
"/apps/screen-sharing/?secret=" +
|
||
|
encodeURIComponent(secret);
|
||
|
fetch("/api/v1/cast", {
|
||
|
method: "POST",
|
||
|
headers: {
|
||
|
"Content-Type": "application/json",
|
||
|
},
|
||
|
body: JSON.stringify({
|
||
|
url: url,
|
||
|
}),
|
||
|
});
|
||
|
});
|
||
|
});
|
||
|
});
|
||
|
});
|
||
|
}
|
||
|
|
||
|
function onSignalReceived(data) {
|
||
|
const message = JSON.parse(data);
|
||
|
switch (message.type) {
|
||
|
case "offer":
|
||
|
if (message.data.secret !== secret) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (receivedOffer || isOffering) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
peerConnection.setRemoteDescription(
|
||
|
new RTCSessionDescription(message.data.offer),
|
||
|
() => {
|
||
|
peerConnection.createAnswer(function (answer) {
|
||
|
peerConnection.setLocalDescription(
|
||
|
answer,
|
||
|
function () {
|
||
|
signaling.send(
|
||
|
JSON.stringify({
|
||
|
type: "answer",
|
||
|
data: {
|
||
|
secret: secret,
|
||
|
answer: answer,
|
||
|
},
|
||
|
})
|
||
|
);
|
||
|
},
|
||
|
error
|
||
|
);
|
||
|
}, error);
|
||
|
},
|
||
|
error
|
||
|
);
|
||
|
|
||
|
receivedOffer = true;
|
||
|
|
||
|
break;
|
||
|
|
||
|
case "answer":
|
||
|
if (receivedAnswer || !isOffering) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (message.data.secret !== secret) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
peerConnection.setRemoteDescription(
|
||
|
new RTCSessionDescription(message.data.answer),
|
||
|
() => {},
|
||
|
error
|
||
|
);
|
||
|
|
||
|
receivedAnswer = true;
|
||
|
|
||
|
break;
|
||
|
|
||
|
case "icecandidate":
|
||
|
if (message.data.candidate) {
|
||
|
peerConnection.addIceCandidate(message.data.candidate);
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
console.log("Received unhandled message", message);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function endCall() {
|
||
|
var videos = document.getElementsByTagName("video");
|
||
|
for (var i = 0; i < videos.length; i++) {
|
||
|
videos[i].pause();
|
||
|
}
|
||
|
|
||
|
peerConnection.close();
|
||
|
}
|
||
|
|
||
|
function error(err) {
|
||
|
console.error(err);
|
||
|
endCall();
|
||
|
}
|
||
|
|
||
|
main();
|