CoPilot Github: Stream-Umstellen
This commit is contained in:
@@ -232,15 +232,19 @@ class CameraSwitch extends EventEmitter {
|
||||
// 1. Live-FFmpeg beenden, auf Prozess-Ende warten (= Device-FD frei)
|
||||
await this._killCurrentAndWait();
|
||||
|
||||
// Kurze Pause: v4l2-Buffer der Kamera können noch Frames der alten Live-
|
||||
// Auflösung enthalten (z.B. 640×480-Rest wenn hires 1920×1080 fordert).
|
||||
// 300 ms genügen damit die Kamera den Format-Reset abschliessen kann.
|
||||
await sleep(300);
|
||||
// 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);
|
||||
}
|
||||
|
||||
this.state = 'grabbing';
|
||||
console.log(`[cam ${this.id}] HD: Live gestoppt nach ${Date.now() - t0}ms, Gerät frei → ${this.hiresSize}-Grab (minWidth=${minWidth})`);
|
||||
|
||||
// 2. hires-FFmpeg starten, warmlaufen lassen, besten Frame greifen
|
||||
// 3. hires-FFmpeg starten, warmlaufen lassen, besten Frame greifen
|
||||
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)`);
|
||||
@@ -269,10 +273,60 @@ 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) => {
|
||||
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) {
|
||||
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) {}
|
||||
}
|
||||
resolve();
|
||||
};
|
||||
|
||||
p.stderr.on('data', (c) => {
|
||||
const s = c.toString();
|
||||
if (/error|busy|invalid|no such|cannot|denied/i.test(s)) console.error(`[cam ${this.id}] warmup ffmpeg: ${s.trim()}`);
|
||||
});
|
||||
p.on('error', done);
|
||||
p.on('close', done);
|
||||
setTimeout(done, 1600);
|
||||
});
|
||||
}
|
||||
|
||||
_captureHires({ minSize, minWidth, settleFrames, maxWaitMs }) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const args = [
|
||||
'-hide_banner', '-loglevel', 'warning',
|
||||
'-fflags', 'nobuffer',
|
||||
'-probesize', '5000000',
|
||||
'-analyzeduration', '1000000',
|
||||
'-f', 'v4l2', '-input_format', 'mjpeg',
|
||||
'-video_size', this.hiresSize, '-framerate', String(this.hiresFps),
|
||||
'-i', this.device,
|
||||
|
||||
Reference in New Issue
Block a user