Claude: WebRTC Versuch
This commit is contained in:
131
server.js
131
server.js
@@ -1,85 +1,72 @@
|
||||
'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 http = require('http');
|
||||
const path = require('path');
|
||||
const { createSnapshotRouter } = require('./src/snapshotService');
|
||||
|
||||
const PORT = parseInt(process.env.PORT ?? '8080', 10);
|
||||
const PORT = parseInt(process.env.PORT ?? '8444', 10);
|
||||
const GO2RTC_URL = process.env.GO2RTC_URL ?? 'http://localhost:1984';
|
||||
|
||||
function buildStreams() {
|
||||
const devices = detectDevices();
|
||||
console.log(`Detected ${devices.length} camera(s):`, devices);
|
||||
const app = express();
|
||||
|
||||
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),
|
||||
}));
|
||||
}
|
||||
app.use(express.static(path.join(__dirname, 'public')));
|
||||
app.use('/api/snapshot', createSnapshotRouter(GO2RTC_URL));
|
||||
|
||||
function main() {
|
||||
const streams = buildStreams();
|
||||
// ── WebRTC signaling proxy ────────────────────────────────────────────────────
|
||||
// Browser postet SDP-Offer hierher; wir leiten es an go2rtc weiter und
|
||||
// geben die SDP-Answer zurück. Nur ein HTTP-Port nach aussen nötig.
|
||||
app.post(
|
||||
'/api/webrtc',
|
||||
express.text({ type: 'application/sdp', limit: '64kb' }),
|
||||
async (req, res) => {
|
||||
const src = req.query.src ?? '';
|
||||
try {
|
||||
const upstream = await fetch(
|
||||
`${GO2RTC_URL}/api/webrtc?src=${encodeURIComponent(src)}`,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/sdp' },
|
||||
body: req.body,
|
||||
}
|
||||
);
|
||||
if (!upstream.ok) {
|
||||
const msg = await upstream.text();
|
||||
return res.status(upstream.status).send(msg);
|
||||
}
|
||||
const answer = await upstream.text();
|
||||
res.set('Content-Type', 'application/sdp');
|
||||
res.send(answer);
|
||||
} catch (err) {
|
||||
res.status(503).json({ error: `go2rtc nicht erreichbar: ${err.message}` });
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// Start all streams eagerly – first client gets immediate frame
|
||||
streams.forEach(s => s.start());
|
||||
// ── Health ────────────────────────────────────────────────────────────────────
|
||||
app.get('/health', async (_req, res) => {
|
||||
let go2rtcOk = false;
|
||||
try {
|
||||
const r = await fetch(`${GO2RTC_URL}/api/streams`);
|
||||
go2rtcOk = r.ok;
|
||||
} catch { /* not reachable */ }
|
||||
|
||||
const app = express();
|
||||
res.json({ status: go2rtcOk ? 'ok' : 'degraded', go2rtc: go2rtcOk });
|
||||
});
|
||||
|
||||
app.use(express.static(path.join(__dirname, 'public')));
|
||||
app.use('/api/snapshot', createSnapshotRouter(streams));
|
||||
// ── Start ─────────────────────────────────────────────────────────────────────
|
||||
const server = http.createServer(app);
|
||||
|
||||
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,
|
||||
})),
|
||||
});
|
||||
});
|
||||
server.listen(PORT, '0.0.0.0', () => {
|
||||
console.log(`AppRobotWebcam on http://0.0.0.0:${PORT}`);
|
||||
console.log(` go2rtc backend: ${GO2RTC_URL}`);
|
||||
console.log(` WebRTC signaling proxy: POST /api/webrtc?src=cam0`);
|
||||
console.log(` Snapshot API: GET /api/snapshot/cam0`);
|
||||
});
|
||||
|
||||
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();
|
||||
const shutdown = (sig) => {
|
||||
console.log(`\n${sig} – shutting down`);
|
||||
server.close(() => process.exit(0));
|
||||
};
|
||||
process.on('SIGINT', () => shutdown('SIGINT'));
|
||||
process.on('SIGTERM', () => shutdown('SIGTERM'));
|
||||
|
||||
Reference in New Issue
Block a user