Claude: fix switch
This commit is contained in:
@@ -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 = [
|
||||
|
||||
Reference in New Issue
Block a user