generated from ztimson/template
Update services/camera.mjs
This commit is contained in:
+27
-11
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
// Camera service (rpicam-apps wrapper)
|
// Camera service (rpicam-apps wrapper)
|
||||||
//
|
//
|
||||||
// libcamera lets only ONE process own the camera, so everything here goes through a single
|
// libcamera lets only ONE process own the camera, so everything here goes through a single
|
||||||
@@ -401,11 +400,21 @@ export class CameraService extends EventEmitter {
|
|||||||
}, this.#cfg.streamIdleStopMs);
|
}, this.#cfg.streamIdleStopMs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Resolve exposure and frame timing once so preview and recording don't silently
|
||||||
|
// choose different shutter speeds or incompatible frame rates.
|
||||||
|
#cameraPlan(settings, requestedFps) {
|
||||||
|
const shutterUs = settings.shutterUs > 0 ? settings.shutterUs : 0;
|
||||||
|
const fps = shutterUs > 0
|
||||||
|
? Math.min(Number(requestedFps), 1_000_000 / shutterUs)
|
||||||
|
: Number(requestedFps);
|
||||||
|
|
||||||
|
return { settings, shutterUs, fps: Math.max(0.001, fps) };
|
||||||
|
}
|
||||||
|
|
||||||
#streamArgs() {
|
#streamArgs() {
|
||||||
const c = this.#cfg;
|
const c = this.#cfg;
|
||||||
const s = this.#settings;
|
const plan = this.#cameraPlan(this.#settings, c.fps);
|
||||||
const shutter = s.shutterUs > 0 ? Math.min(s.shutterUs, c.streamMaxShutterUs) : 0;
|
const { settings, shutterUs, fps } = plan;
|
||||||
const fps = shutter > 0 ? Math.min(c.fps, 1_000_000 / shutter) : c.fps;
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
fps,
|
fps,
|
||||||
@@ -415,8 +424,8 @@ export class CameraService extends EventEmitter {
|
|||||||
'--height', String(c.height),
|
'--height', String(c.height),
|
||||||
'--framerate', fps.toFixed(3),
|
'--framerate', fps.toFixed(3),
|
||||||
'--codec', 'mjpeg',
|
'--codec', 'mjpeg',
|
||||||
'-q', String(s.quality),
|
'-q', String(settings.quality),
|
||||||
...this.#controlArgs(s, { shutterUs: shutter }),
|
...this.#controlArgs(settings, { shutterUs }),
|
||||||
'-o', '-',
|
'-o', '-',
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
@@ -424,7 +433,7 @@ export class CameraService extends EventEmitter {
|
|||||||
|
|
||||||
#startStream() {
|
#startStream() {
|
||||||
const { fps, args } = this.#streamArgs();
|
const { fps, args } = this.#streamArgs();
|
||||||
this.#log.info(`starting live view ${this.#cfg.width}x${this.#cfg.height} @ ${fps.toFixed(1)} fps`);
|
this.#log.info(`starting live view ${this.#cfg.width}x${this.#cfg.height} @ ${fps.toFixed(3)} fps`);
|
||||||
|
|
||||||
const proc = spawn(this.#cfg.vidBin, args, { stdio: ['ignore', 'pipe', 'pipe'] });
|
const proc = spawn(this.#cfg.vidBin, args, { stdio: ['ignore', 'pipe', 'pipe'] });
|
||||||
const stream = { proc, fps, expected: false, startedAt: Date.now(), frames: 0, exited: null };
|
const stream = { proc, fps, expected: false, startedAt: Date.now(), frames: 0, exited: null };
|
||||||
@@ -584,7 +593,8 @@ export class CameraService extends EventEmitter {
|
|||||||
|
|
||||||
#stillArgs(file, settings, { raw = false } = {}) {
|
#stillArgs(file, settings, { raw = false } = {}) {
|
||||||
const c = this.#cfg;
|
const c = this.#cfg;
|
||||||
const manual = settings.shutterUs > 0 && settings.gain > 0;
|
const { shutterUs } = this.#cameraPlan(settings, 1);
|
||||||
|
const manual = shutterUs > 0 && settings.gain > 0;
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'-n',
|
'-n',
|
||||||
@@ -681,7 +691,9 @@ export class CameraService extends EventEmitter {
|
|||||||
|
|
||||||
const w = width || c.videoWidth;
|
const w = width || c.videoWidth;
|
||||||
const h = height || c.videoHeight;
|
const h = height || c.videoHeight;
|
||||||
const f = fps || c.videoFps;
|
const requestedFps = Number(fps || c.videoFps);
|
||||||
|
const plan = this.#cameraPlan(settings, requestedFps);
|
||||||
|
const f = plan.fps;
|
||||||
const base = `video_${stamp()}`;
|
const base = `video_${stamp()}`;
|
||||||
const rawFile = path.join(this.#dirs.videos, `${base}.h264`);
|
const rawFile = path.join(this.#dirs.videos, `${base}.h264`);
|
||||||
|
|
||||||
@@ -696,7 +708,7 @@ export class CameraService extends EventEmitter {
|
|||||||
'--framerate', String(f),
|
'--framerate', String(f),
|
||||||
'--codec', 'h264',
|
'--codec', 'h264',
|
||||||
'--inline',
|
'--inline',
|
||||||
...this.#controlArgs(settings),
|
...this.#controlArgs(plan.settings, { shutterUs: plan.shutterUs }),
|
||||||
'-o', rawFile,
|
'-o', rawFile,
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -750,7 +762,11 @@ export class CameraService extends EventEmitter {
|
|||||||
proc.once('close', (code, signal) => finish(code, signal));
|
proc.once('close', (code, signal) => finish(code, signal));
|
||||||
});
|
});
|
||||||
|
|
||||||
this.#log.info(`video recording started (${w}x${h} @ ${f} fps${durationSec ? `, ${durationSec}s` : ''})`);
|
this.#log.info(
|
||||||
|
`video recording started (${w}x${h} @ ${f.toFixed(3)} fps` +
|
||||||
|
`${f < requestedFps ? `, limited from ${requestedFps} fps by ${plan.shutterUs}µs shutter` : ''}` +
|
||||||
|
`${durationSec ? `, ${durationSec}s` : ''})`,
|
||||||
|
);
|
||||||
return this.#jobSummary();
|
return this.#jobSummary();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user