Claude: fix switch

This commit is contained in:
chk
2026-06-06 12:45:24 +02:00
parent 86025d22fa
commit b0b412d4ee

View File

@@ -229,24 +229,18 @@ class CameraSwitch extends EventEmitter {
if (this.restartTimer) { clearTimeout(this.restartTimer); this.restartTimer = null; } if (this.restartTimer) { clearTimeout(this.restartTimer); this.restartTimer = null; }
try { try {
// 1. Live-FFmpeg beenden, auf Prozess-Ende warten (= Device-FD frei) // 1. Live-FFmpeg beenden, auf Prozess-Ende warten (= Device-FD frei).
// Das close-Event ist der harte Beweis, dass der FD zu ist → kein zweiter
// Öffner. KEIN warmup/sleep: auf dem Host bestätigt (A/B-Test 2026-06-06),
// dass ein direkter Open auf die Zielauflösung sofort korrekte Frames liefert
// (1920×1080 bzw. 1280×960, jedes Frame). Ein zweites ffmpeg dazwischen
// erzeugt nur „Device or resource busy".
await this._killCurrentAndWait(); await this._killCurrentAndWait();
// 2. Kamera resetten: kurz warten, dann Zwischenformat öffnen, wieder warten.
await sleep(800);
const warmupSize = this._chooseWarmupSize();
if (warmupSize) {
console.log(`[cam ${this.id}] HD: Zwischenformat ${warmupSize} zum Kamera-Reset`);
await this._warmupFormat(warmupSize);
await sleep(500);
} else {
console.log(`[cam ${this.id}] HD: Kein Zwischenformat nötig (live ${this.liveSize} → hires ${this.hiresSize})`);
}
this.state = 'grabbing'; this.state = 'grabbing';
console.log(`[cam ${this.id}] HD: Live gestoppt nach ${Date.now() - t0}ms, Gerät frei → ${this.hiresSize}-Grab (minWidth=${minWidth})`); console.log(`[cam ${this.id}] HD: Live gestoppt nach ${Date.now() - t0}ms, Gerät frei → ${this.hiresSize}-Grab (minWidth=${minWidth}, encode=${this.hiresEncode})`);
// 3. hires-FFmpeg starten, warmlaufen lassen, besten Frame greifen // 2. hires-FFmpeg starten, warmlaufen lassen (settleFrames), besten Frame greifen.
// minWidth lehnt etwaige Übergangs-Frames in falscher Auflösung ab.
const jpeg = await this._captureHires({ minSize, minWidth, settleFrames, maxWaitMs }); const jpeg = await this._captureHires({ minSize, minWidth, settleFrames, maxWaitMs });
const gotW = readJpegWidth(jpeg) ?? '?'; const gotW = readJpegWidth(jpeg) ?? '?';
console.log(`[cam ${this.id}] HD OK ${jpeg.length} bytes, Breite=${gotW}px (Soll: ${hiresW}px, ${Date.now() - t0}ms)`); console.log(`[cam ${this.id}] HD OK ${jpeg.length} bytes, Breite=${gotW}px (Soll: ${hiresW}px, ${Date.now() - t0}ms)`);
@@ -275,61 +269,6 @@ class CameraSwitch extends EventEmitter {
}); });
} }
_chooseWarmupSize() {
const liveW = parseInt(this.liveSize.split('x')[0], 10);
const hiresW = parseInt(this.hiresSize.split('x')[0], 10);
if (Number.isNaN(liveW) || Number.isNaN(hiresW)) return null;
if (liveW < 1280 && hiresW >= 1280) return '1280x720';
return null;
}
_warmupFormat(size) {
return new Promise((resolve) => {
console.log(`[cam ${this.id}] HD: Warmup-Format ${size} starten`);
const args = [
'-hide_banner', '-loglevel', 'warning',
'-fflags', 'nobuffer',
'-f', 'v4l2', '-input_format', 'mjpeg',
'-video_size', size, '-framerate', String(this.hiresFps),
'-i', this.device,
'-frames:v', '4', '-f', 'null', '-'
];
let p;
try {
p = spawn('ffmpeg', args, { stdio: ['ignore', 'pipe', 'ignore'] });
} catch (_e) {
console.warn(`[cam ${this.id}] warmup ffmpeg start fehlgeschlagen: ${_e.message}`);
return resolve();
}
this.proc = p;
this.stopping = false;
let finished = false;
const done = () => {
if (finished) return;
finished = true;
if (p) {
try { p.kill('SIGTERM'); } catch (_e) {}
}
console.log(`[cam ${this.id}] HD: Warmup-Format ${size} beendet`);
resolve();
};
p.stderr.on('data', (c) => {
const s = c.toString().trim();
if (!s) return;
if (/error|busy|invalid|no such|cannot|denied/i.test(s)) {
console.warn(`[cam ${this.id}] warmup ffmpeg: ${s}`);
} else {
console.log(`[cam ${this.id}] warmup ffmpeg: ${s}`);
}
});
p.on('error', done);
p.on('close', done);
setTimeout(done, 1600);
});
}
_captureHires({ minSize, minWidth, settleFrames, maxWaitMs }) { _captureHires({ minSize, minWidth, settleFrames, maxWaitMs }) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const args = [ const args = [