From b0b412d4eed996b880c3c7c852f993388f061a61 Mon Sep 17 00:00:00 2001 From: chk <79915315+ChKendel@users.noreply.github.com> Date: Sat, 6 Jun 2026 12:45:24 +0200 Subject: [PATCH] Claude: fix switch --- src/cameraSwitch.js | 79 ++++++--------------------------------------- 1 file changed, 9 insertions(+), 70 deletions(-) diff --git a/src/cameraSwitch.js b/src/cameraSwitch.js index f388b10..b0601f7 100644 --- a/src/cameraSwitch.js +++ b/src/cameraSwitch.js @@ -229,24 +229,18 @@ class CameraSwitch extends EventEmitter { if (this.restartTimer) { clearTimeout(this.restartTimer); this.restartTimer = null; } 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(); - - // 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'; - 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 gotW = readJpegWidth(jpeg) ?? '?'; 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 }) { return new Promise((resolve, reject) => { const args = [