2022-12-15 23:50:47 +00:00
|
|
|
<!DOCTYPE html>
|
|
|
|
<html>
|
|
|
|
<head>
|
|
|
|
<meta charset="utf-8">
|
|
|
|
<style>
|
|
|
|
html, body {
|
|
|
|
margin: 0;
|
|
|
|
padding: 0;
|
|
|
|
height: 100%;
|
|
|
|
overflow: hidden;
|
|
|
|
}
|
|
|
|
#video {
|
|
|
|
width: 100%;
|
|
|
|
height: 100%;
|
|
|
|
background: black;
|
|
|
|
}
|
|
|
|
</style>
|
|
|
|
</head>
|
|
|
|
<body>
|
|
|
|
|
|
|
|
<video id="video" muted controls autoplay playsinline></video>
|
|
|
|
|
|
|
|
<script>
|
|
|
|
|
|
|
|
const restartPause = 2000;
|
|
|
|
|
|
|
|
class Receiver {
|
|
|
|
constructor() {
|
|
|
|
this.terminated = false;
|
|
|
|
this.ws = null;
|
|
|
|
this.pc = null;
|
|
|
|
this.restartTimeout = null;
|
|
|
|
this.start();
|
|
|
|
}
|
|
|
|
|
|
|
|
start() {
|
|
|
|
console.log("connecting");
|
|
|
|
|
|
|
|
this.ws = new WebSocket(window.location.href.replace(/^http/, "ws") + 'ws');
|
|
|
|
|
|
|
|
this.ws.onerror = () => {
|
|
|
|
console.log("ws error");
|
|
|
|
if (this.ws === null) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
this.ws.close();
|
|
|
|
this.ws = null;
|
|
|
|
};
|
|
|
|
|
|
|
|
this.ws.onclose = () => {
|
|
|
|
console.log("ws closed");
|
|
|
|
this.ws = null;
|
|
|
|
this.scheduleRestart();
|
|
|
|
};
|
|
|
|
|
|
|
|
this.ws.onmessage = (msg) => this.onIceServers(msg);
|
|
|
|
}
|
|
|
|
|
|
|
|
onIceServers(msg) {
|
|
|
|
if (this.ws === null) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const iceServers = JSON.parse(msg.data);
|
|
|
|
|
|
|
|
this.pc = new RTCPeerConnection({
|
|
|
|
iceServers,
|
|
|
|
});
|
|
|
|
|
|
|
|
this.ws.onmessage = (msg) => this.onRemoteDescription(msg);
|
|
|
|
this.pc.onicecandidate = (evt) => this.onIceCandidate(evt);
|
|
|
|
|
|
|
|
this.pc.oniceconnectionstatechange = () => {
|
|
|
|
if (this.pc === null) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
console.log("peer connection state:", this.pc.iceConnectionState);
|
|
|
|
|
|
|
|
switch (this.pc.iceConnectionState) {
|
|
|
|
case "connected":
|
|
|
|
this.pc.onicecandidate = undefined;
|
|
|
|
this.ws.onmessage = undefined;
|
2022-12-20 12:23:53 +00:00
|
|
|
this.ws.onerror = undefined
|
2022-12-15 23:50:47 +00:00
|
|
|
this.ws.onclose = undefined;
|
2022-12-20 12:23:53 +00:00
|
|
|
// do not close the WebSocket connection
|
|
|
|
// in order to allow the other side of the connection
|
|
|
|
// to switch to the "connected" state before WebSocket is closed.
|
2022-12-15 23:50:47 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case "disconnected":
|
|
|
|
this.scheduleRestart();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
this.pc.ontrack = (evt) => {
|
|
|
|
console.log("new track " + evt.track.kind);
|
2022-12-19 22:08:24 +00:00
|
|
|
document.getElementById("video").srcObject = evt.streams[0];
|
2022-12-15 23:50:47 +00:00
|
|
|
};
|
|
|
|
|
2022-12-19 21:13:54 +00:00
|
|
|
const direction = "sendrecv";
|
2022-12-15 23:50:47 +00:00
|
|
|
this.pc.addTransceiver("video", { direction });
|
|
|
|
this.pc.addTransceiver("audio", { direction });
|
|
|
|
|
|
|
|
this.pc.createOffer()
|
|
|
|
.then((desc) => {
|
|
|
|
if (this.pc === null || this.ws === null) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
this.pc.setLocalDescription(desc);
|
|
|
|
|
|
|
|
console.log("sending offer");
|
|
|
|
this.ws.send(JSON.stringify(desc));
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
onRemoteDescription(msg) {
|
|
|
|
if (this.pc === null || this.ws === null) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
this.pc.setRemoteDescription(new RTCSessionDescription(JSON.parse(msg.data)));
|
|
|
|
this.ws.onmessage = (msg) => this.onRemoteCandidate(msg);
|
|
|
|
}
|
|
|
|
|
|
|
|
onIceCandidate(evt) {
|
|
|
|
if (this.ws === null) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (evt.candidate !== null) {
|
|
|
|
if (evt.candidate.candidate !== "") {
|
|
|
|
this.ws.send(JSON.stringify(evt.candidate));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
onRemoteCandidate(msg) {
|
|
|
|
if (this.pc === null) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
this.pc.addIceCandidate(JSON.parse(msg.data));
|
|
|
|
}
|
|
|
|
|
|
|
|
scheduleRestart() {
|
|
|
|
if (this.terminated) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this.ws !== null) {
|
|
|
|
this.ws.close();
|
|
|
|
this.ws = null;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this.pc !== null) {
|
|
|
|
this.pc.close();
|
|
|
|
this.pc = null;
|
|
|
|
}
|
|
|
|
|
|
|
|
this.restartTimeout = window.setTimeout(() => {
|
|
|
|
this.restartTimeout = null;
|
|
|
|
this.start();
|
|
|
|
}, restartPause);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
window.addEventListener('DOMContentLoaded', () => new Receiver());
|
|
|
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
</body>
|
|
|
|
</html>
|