Files
appRobotWebcam/public/viewer.js

111 lines
3.3 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
'use strict';
const WS_RECONNECT_MS = 2000;
function createCameraView(idx, container) {
// DOM
const box = document.createElement('div');
box.className = 'cam-box';
const canvas = document.createElement('canvas');
canvas.width = 640;
canvas.height = 480;
box.appendChild(canvas);
const label = document.createElement('div');
label.className = 'cam-label';
label.textContent = `cam${idx}`;
box.appendChild(label);
const info = document.createElement('div');
info.className = 'cam-info';
info.textContent = 'Verbinde...';
box.appendChild(info);
const actions = document.createElement('div');
actions.className = 'cam-actions';
const snapBtn = document.createElement('button');
snapBtn.textContent = 'Snapshot';
actions.appendChild(snapBtn);
box.appendChild(actions);
container.appendChild(box);
// Snapshot download
snapBtn.addEventListener('click', () => {
const a = document.createElement('a');
a.href = `/api/snapshot/cam${idx}`;
a.download = `cam${idx}_${Date.now()}.jpg`;
a.click();
});
// Rendering
const ctx = canvas.getContext('2d');
let frameCount = 0;
let lastFpsTs = Date.now();
let fps = 0;
function drawFrame(arrayBuffer) {
const blob = new Blob([arrayBuffer], { type: 'image/jpeg' });
createImageBitmap(blob)
.then((bmp) => {
if (canvas.width !== bmp.width || canvas.height !== bmp.height) {
canvas.width = bmp.width;
canvas.height = bmp.height;
}
ctx.drawImage(bmp, 0, 0);
bmp.close();
frameCount++;
const now = Date.now();
if (now - lastFpsTs >= 1000) {
fps = Math.round(frameCount * 1000 / (now - lastFpsTs));
frameCount = 0;
lastFpsTs = now;
info.textContent = `${fps} fps`;
}
})
.catch(() => {/* ignore decode errors */});
}
// WebSocket connection with auto-reconnect
function connect() {
const proto = location.protocol === 'https:' ? 'wss:' : 'ws:';
const ws = new WebSocket(`${proto}//${location.host}/ws/cam${idx}`);
ws.binaryType = 'arraybuffer';
ws.onopen = () => { info.textContent = 'Verbunden'; };
ws.onclose = () => {
info.textContent = `Getrennt neu in ${WS_RECONNECT_MS / 1000}s`;
setTimeout(connect, WS_RECONNECT_MS);
};
ws.onerror = () => { info.textContent = 'Verbindungsfehler'; };
ws.onmessage = (evt) => drawFrame(evt.data);
}
connect();
}
// Fetch camera list from server, then build one view per camera
fetch('/api/snapshot')
.then(r => r.json())
.then(data => {
const container = document.getElementById('cameras');
const count = data.cameras?.length ?? 0;
if (count === 0) {
document.getElementById('statusText').textContent = 'Keine Kameras erkannt';
return;
}
for (let i = 0; i < count; i++) createCameraView(i, container);
document.getElementById('statusText').textContent =
`${count} Kamera${count !== 1 ? 's' : ''} erkannt`;
})
.catch(() => {
// Fallback: show 2 cameras if API fails
const container = document.getElementById('cameras');
for (let i = 0; i < 2; i++) createCameraView(i, container);
document.getElementById('statusText').textContent = 'Kamera-API nicht erreichbar';
});