fix
All checks were successful
Create and publish a Docker image 🚀 / build-and-push-image (push) Successful in 1m17s
All checks were successful
Create and publish a Docker image 🚀 / build-and-push-image (push) Successful in 1m17s
This commit is contained in:
@@ -1,92 +1,124 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { onMount } from "svelte";
|
import { onMount } from "svelte";
|
||||||
|
|
||||||
let localVideo: HTMLVideoElement;
|
let localVideo: HTMLVideoElement;
|
||||||
let localStream: MediaStream;
|
let localStream: MediaStream;
|
||||||
let pc: RTCPeerConnection;
|
|
||||||
let ws: WebSocket;
|
|
||||||
|
|
||||||
function createPC() {
|
let ws: WebSocket;
|
||||||
pc = new RTCPeerConnection({
|
let pc: RTCPeerConnection;
|
||||||
iceServers: [{ urls: "stun:stun.l.google.com:19302" }],
|
|
||||||
});
|
|
||||||
|
|
||||||
pc.ontrack = (e) => {
|
/* ---------- helpers ---------- */
|
||||||
const video = document.createElement("video");
|
|
||||||
video.autoplay = true;
|
|
||||||
video.playsInline = true;
|
|
||||||
video.srcObject = e.streams[0];
|
|
||||||
document.body.appendChild(video);
|
|
||||||
};
|
|
||||||
|
|
||||||
pc.onicecandidate = (e) => {
|
function waitForWsOpen(ws: WebSocket): Promise<void> {
|
||||||
if (e.candidate) {
|
return new Promise((resolve) => {
|
||||||
ws.send(
|
if (ws.readyState === WebSocket.OPEN) {
|
||||||
JSON.stringify({
|
resolve();
|
||||||
type: "ice",
|
} else {
|
||||||
data: e.candidate,
|
ws.addEventListener("open", () => resolve(), { once: true });
|
||||||
}),
|
}
|
||||||
);
|
});
|
||||||
}
|
}
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
async function negotiate() {
|
/* ---------- WebRTC ---------- */
|
||||||
const offer = await pc.createOffer();
|
|
||||||
await pc.setLocalDescription(offer);
|
|
||||||
|
|
||||||
ws.send(
|
function createPeerConnection() {
|
||||||
JSON.stringify({
|
pc = new RTCPeerConnection({
|
||||||
type: "offer",
|
iceServers: [{ urls: "stun:stun.l.google.com:19302" }],
|
||||||
data: offer,
|
});
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
onMount(async () => {
|
pc.ontrack = (e) => {
|
||||||
localStream = await navigator.mediaDevices.getUserMedia({
|
const video = document.createElement("video");
|
||||||
video: true,
|
video.autoplay = true;
|
||||||
audio: true,
|
video.playsInline = true;
|
||||||
});
|
video.srcObject = e.streams[0];
|
||||||
|
document.body.appendChild(video);
|
||||||
|
};
|
||||||
|
|
||||||
localVideo.srcObject = localStream;
|
pc.onicecandidate = (e) => {
|
||||||
|
if (!e.candidate) return;
|
||||||
|
if (ws.readyState !== WebSocket.OPEN) return;
|
||||||
|
|
||||||
ws = new WebSocket("wss://meet-api.quizer.space/ws");
|
ws.send(
|
||||||
|
JSON.stringify({
|
||||||
|
type: "ice",
|
||||||
|
data: e.candidate,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
createPC();
|
async function negotiate() {
|
||||||
|
const offer = await pc.createOffer();
|
||||||
|
await pc.setLocalDescription(offer);
|
||||||
|
|
||||||
for (const track of localStream.getTracks()) {
|
ws.send(
|
||||||
pc.addTrack(track, localStream);
|
JSON.stringify({
|
||||||
}
|
type: "offer",
|
||||||
|
data: offer,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
ws.onmessage = async (e) => {
|
/* ---------- lifecycle ---------- */
|
||||||
const msg = JSON.parse(e.data);
|
|
||||||
|
|
||||||
switch (msg.type) {
|
onMount(async () => {
|
||||||
case "answer":
|
/* 1. media */
|
||||||
await pc.setRemoteDescription(msg.data);
|
localStream = await navigator.mediaDevices.getUserMedia({
|
||||||
break;
|
video: true,
|
||||||
|
audio: true,
|
||||||
|
});
|
||||||
|
|
||||||
case "ice":
|
localVideo.srcObject = localStream;
|
||||||
await pc.addIceCandidate(msg.data);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "renegotiate":
|
/* 2. websocket */
|
||||||
await negotiate();
|
ws = new WebSocket("wss://meet-api.quizer.space/ws");
|
||||||
break;
|
await waitForWsOpen(ws);
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
await negotiate();
|
console.log("[ws] connected");
|
||||||
});
|
|
||||||
|
/* 3. peer connection */
|
||||||
|
createPeerConnection();
|
||||||
|
|
||||||
|
for (const track of localStream.getTracks()) {
|
||||||
|
pc.addTrack(track, localStream);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 4. ws messages */
|
||||||
|
ws.onmessage = async (e) => {
|
||||||
|
const msg = JSON.parse(e.data);
|
||||||
|
console.log("[ws] <=", msg.type);
|
||||||
|
|
||||||
|
switch (msg.type) {
|
||||||
|
case "answer":
|
||||||
|
await pc.setRemoteDescription(msg.data);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "ice":
|
||||||
|
await pc.addIceCandidate(msg.data);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "renegotiate":
|
||||||
|
await negotiate();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/* 5. initial offer */
|
||||||
|
await negotiate();
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<video bind:this={localVideo} autoplay muted playsinline></video>
|
<video
|
||||||
|
bind:this={localVideo}
|
||||||
|
autoplay
|
||||||
|
muted
|
||||||
|
playsinline
|
||||||
|
></video>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
video {
|
video {
|
||||||
width: 240px;
|
width: 240px;
|
||||||
margin: 8px;
|
margin: 8px;
|
||||||
background: black;
|
background: black;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
Reference in New Issue
Block a user