import "../styles/app.css"; const activeStreams: MediaStream[] = []; function now(): string { return new Date().toLocaleTimeString(); } function appendLog(output: HTMLPreElement, message: string): void { output.textContent = `${output.textContent}[${now()}] ${message}\n`; output.scrollTop = output.scrollHeight; } function setStatus(status: HTMLElement, value: string): void { status.textContent = value; } function stopAllStreams(video: HTMLVideoElement, output: HTMLPreElement, status: HTMLElement): void { for (const stream of activeStreams.splice(0)) { for (const track of stream.getTracks()) { track.stop(); } } video.pause(); video.srcObject = null; setStatus(status, "idle"); appendLog(output, "Stopped all active tracks"); } async function probeDevices(output: HTMLPreElement): Promise { if (!("mediaDevices" in navigator) || typeof navigator.mediaDevices.enumerateDevices !== "function") { appendLog(output, "enumerateDevices is unavailable"); return; } try { const devices = await navigator.mediaDevices.enumerateDevices(); const summary = devices .map((device) => `${device.kind} ${device.label || "(label hidden until permission)"}`) .join(" | "); appendLog(output, `Devices: ${summary || "none"}`); } catch (err) { appendLog(output, `enumerateDevices failed: ${String(err)}`); appendLog(output, "Devices: none"); } } async function requestUserMedia(video: HTMLVideoElement, output: HTMLPreElement, status: HTMLElement): Promise { if (!("mediaDevices" in navigator) || typeof navigator.mediaDevices.getUserMedia !== "function") { appendLog(output, "getUserMedia is unavailable"); return; } try { setStatus(status, "requesting camera + microphone"); const stream = await navigator.mediaDevices.getUserMedia({ audio: true, video: true }); activeStreams.push(stream); video.srcObject = stream; await video.play(); setStatus(status, "camera + microphone granted"); appendLog(output, "camera + microphone permission granted"); await probeDevices(output); } catch (err) { setStatus(status, "camera + microphone blocked"); appendLog(output, `camera + microphone request failed: ${String(err)}`); } } async function requestDisplayMedia(video: HTMLVideoElement, output: HTMLPreElement, status: HTMLElement): Promise { if (!("mediaDevices" in navigator) || typeof navigator.mediaDevices.getDisplayMedia !== "function") { appendLog(output, "getDisplayMedia is unavailable"); return; } try { setStatus(status, "requesting screen capture"); const stream = await navigator.mediaDevices.getDisplayMedia({ video: true, audio: true }); activeStreams.push(stream); video.srcObject = stream; await video.play(); setStatus(status, "screen capture granted"); appendLog(output, "screen capture permission granted"); } catch (err) { setStatus(status, "screen capture blocked"); appendLog(output, `screen capture request failed: ${String(err)}`); } } function mount(): void { document.title = "WebRTC Diagnostics"; document.body.innerHTML = `

WebRTC Diagnostics

Use this page to validate camera, microphone, and screen permission behavior.

Status: idle
Event Log
[${now()}] Ready\n
`; const status = document.getElementById("status"); const preview = document.getElementById("preview"); const output = document.getElementById("output"); const btnAV = document.getElementById("btn-av"); const btnScreen = document.getElementById("btn-screen"); const btnStop = document.getElementById("btn-stop"); if (!(status instanceof HTMLElement)) { return; } if (!(preview instanceof HTMLVideoElement)) { return; } if (!(output instanceof HTMLPreElement)) { return; } if (!(btnAV instanceof HTMLButtonElement)) { return; } if (!(btnScreen instanceof HTMLButtonElement)) { return; } if (!(btnStop instanceof HTMLButtonElement)) { return; } btnAV.addEventListener("click", () => { requestUserMedia(preview, output, status).catch((err: unknown) => { appendLog(output, `unexpected user media error: ${String(err)}`); }); }); btnScreen.addEventListener("click", () => { requestDisplayMedia(preview, output, status).catch((err: unknown) => { appendLog(output, `unexpected display media error: ${String(err)}`); }); }); btnStop.addEventListener("click", () => { stopAllStreams(preview, output, status); }); window.addEventListener("beforeunload", () => { stopAllStreams(preview, output, status); }); } mount();