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; }
|
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 = [
|
||||||
|
|||||||
Reference in New Issue
Block a user