WebCam als schlanke alternative zum appVideoControl

This commit is contained in:
chk
2026-06-02 22:19:08 +02:00
commit 9b1ae2ae14
11 changed files with 842 additions and 0 deletions

85
server.js Normal file
View File

@@ -0,0 +1,85 @@
'use strict';
const express = require('express');
const http = require('http');
const { WebSocketServer } = require('ws');
const path = require('path');
const { detectDevices } = require('./src/deviceDetect');
const { VideoStream } = require('./src/videoStream');
const { createSnapshotRouter } = require('./src/snapshotService');
const PORT = parseInt(process.env.PORT ?? '8080', 10);
function buildStreams() {
const devices = detectDevices();
console.log(`Detected ${devices.length} camera(s):`, devices);
return devices.map((device, idx) => new VideoStream(device, {
name: `cam${idx}`,
width: parseInt(process.env[`CAM${idx}_WIDTH`] ?? '640', 10),
height: parseInt(process.env[`CAM${idx}_HEIGHT`] ?? '480', 10),
fps: parseInt(process.env[`CAM${idx}_FPS`] ?? '30', 10),
quality: parseInt(process.env[`CAM${idx}_QUALITY`] ?? '5', 10),
}));
}
function main() {
const streams = buildStreams();
// Start all streams eagerly first client gets immediate frame
streams.forEach(s => s.start());
const app = express();
app.use(express.static(path.join(__dirname, 'public')));
app.use('/api/snapshot', createSnapshotRouter(streams));
app.get('/health', (_req, res) => {
res.json({
status: 'ok',
cameras: streams.map((s, i) => ({
id: `cam${i}`,
device: s.device,
running: s.isRunning,
clients: s.clientCount,
hasFrame: s.latestFrame !== null,
})),
});
});
const server = http.createServer(app);
const wss = new WebSocketServer({ noServer: true });
server.on('upgrade', (req, socket, head) => {
const match = req.url?.match(/^\/ws\/cam(\d+)$/);
if (!match) { socket.destroy(); return; }
const idx = parseInt(match[1], 10);
if (!streams[idx]) { socket.destroy(); return; }
wss.handleUpgrade(req, socket, head, (ws) => {
const stream = streams[idx];
stream.addClient(ws);
ws.on('close', () => stream.removeClient(ws));
ws.on('error', () => stream.removeClient(ws));
});
});
server.listen(PORT, '0.0.0.0', () => {
console.log(`AppRobotWebcam on http://0.0.0.0:${PORT}`);
streams.forEach((s, i) => {
console.log(` ws://0.0.0.0:${PORT}/ws/cam${i}${s.device}`);
console.log(` http://0.0.0.0:${PORT}/api/snapshot/cam${i}`);
});
});
const shutdown = (signal) => {
console.log(`\n${signal} received shutting down`);
streams.forEach(s => s.stop());
server.close(() => process.exit(0));
};
process.on('SIGINT', () => shutdown('SIGINT'));
process.on('SIGTERM', () => shutdown('SIGTERM'));
}
main();