filmout_manager/dist/sequence/index.js

385 lines
12 KiB
JavaScript
Raw Normal View History

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Sequence = void 0;
const files_1 = require("../files");
const log_1 = require("../log");
var SequenceStatus;
(function (SequenceStatus) {
SequenceStatus[SequenceStatus["IDLE"] = 0] = "IDLE";
SequenceStatus[SequenceStatus["RUNNING"] = 1] = "RUNNING";
SequenceStatus[SequenceStatus["PAUSED"] = 2] = "PAUSED";
})(SequenceStatus || (SequenceStatus = {}));
class Statistics {
constructor(exposure, frames) {
this.exposure = exposure;
this.frames = frames;
this.frameLoad = [];
this.frameOpen = [];
this.frameExposureElapsed = [];
this.frameExposureReported = [];
this.frameClose = [];
this.frameTotal = [];
}
add(load, open, exposureElapsed, exposureReported, close, total) {
this.frameLoad.push(load);
this.frameOpen.push(open);
this.frameExposureElapsed.push(exposureElapsed);
this.frameExposureReported.push(exposureReported);
this.frameClose.push(close);
this.frameTotal.push(total);
}
average(arr) {
return arr.reduce((a, b) => a + b) / arr.length;
}
elapsed() {
return this.frameTotal.reduce((a, b) => a + b);
}
estimate(frame, avg) {
const frames = this.frames - frame;
return frames * avg;
}
margin(target, arr) {
const min = Math.min(...arr);
const max = Math.max(...arr);
const diff = Math.abs(max - min);
return (diff / target) / 2;
}
calculate(frame) {
if (this.frameTotal.length === 0) {
return null;
}
const totalFrameAvg = this.average(this.frameTotal);
const openAvg = this.average(this.frameOpen);
const closeAvg = this.average(this.frameClose);
const exposureAvg = this.average(this.frameExposureReported);
const loadAvg = this.average(this.frameLoad);
return {
totalFrameLast: this.frameTotal[this.frameTotal.length - 1],
totalFrameAvg,
totalFrameMargin: this.margin(totalFrameAvg, this.frameTotal) * 100.0,
fps: 1000.0 / totalFrameAvg,
loadLast: this.frameLoad[this.frameLoad.length - 1],
loadAvg,
loadMargin: this.margin(loadAvg, this.frameLoad),
openLast: this.frameOpen[this.frameOpen.length - 1],
openAvg,
openMargin: this.margin(openAvg, this.frameOpen) * 100.0,
closeLast: this.frameClose[this.frameClose.length - 1],
closeAvg,
closeMargin: this.margin(closeAvg, this.frameClose) * 100.0,
exposureLast: this.frameExposureReported[this.frameExposureReported.length - 1],
exposureAvg,
exposureMargin: this.margin(exposureAvg, this.frameExposureReported) * 100.0,
elapsed: this.elapsed(),
estimate: this.estimate(frame, totalFrameAvg)
};
}
}
class Sequence {
constructor(camera, fd, display, ffprobe, send) {
this.current = null;
this.info = null;
this.images = [];
this.stats = null;
this.running = false;
this.paused = false;
this.progress = 0;
this.frame = 0;
this.frames = 0;
this.exposure = 1000;
this.log = (0, log_1.createLog)('seq');
2024-08-05 02:34:03 +00:00
this.camera = camera;
this.fd = fd;
this.display = display;
this.ffprobe = ffprobe;
this.send = send;
}
start() {
if (this.current !== null) {
this.running = true;
this.log.info(`Started sequence: ${this.current.name}`);
this.stats = new Statistics(this.exposure, this.frames);
this.run();
}
}
stop() {
if (this.running && this.current !== null) {
this.log.info(`Stopped sequence: ${this.current.name}`);
this.running = false;
}
}
isRunning() {
return this.running;
}
isLoaded() {
return this.current !== null;
}
async run() {
//update running
for (let i = this.frame; i < this.images.length; i++) {
if (!this.running) {
break;
}
try {
await this.frameRecord();
}
catch (err) {
this.log.error(`Error recording frame`, err);
}
this.frameAdvance();
}
//complete running
this.updateClientsOnState();
this.stats = null;
}
async load(seq) {
this.current = seq;
this.frame = 0;
this.progress = 0;
await this.enumerate();
this.updateClientsOnLoad();
}
updateClientsOnLoad() {
if (this.current !== null) {
this.send({ cmd: 'select', state: this.getState() });
}
}
updateClientsOnState() {
if (this.current !== null) {
this.send({ cmd: 'update', state: this.getState() });
}
}
updateClientsOnDisplay() {
if (this.current !== null) {
this.send({ cmd: 'display', state: this.getState() });
}
}
async enumerate() {
let screen;
if (this.current === null) {
this.log.error('Cannot enumerate sequence because it is not set');
return;
}
try {
this.images = await files_1.Files.enumerateSequence(this.current.path);
}
catch (err) {
this.log.error(`Error enumerating images in sequence: ${this.current.name}`, err);
return;
}
this.frames = this.images.length;
this.log.info(`Sequence ${this.current.name} contains ${this.images.length} image${this.images.length === 1 ? '' : 's'}`);
if (this.frames > 0) {
this.info = await this.ffprobe.info(this.images[0].path);
}
if (this.info !== null) {
screen = this.display.getScreen();
this.display.setSource(this.info.width, this.info.height);
this.log.info(`Screen : ${screen.width},${screen.height}`);
this.log.info(`Sequence : ${this.info.width},${this.info.height}`);
this.log.info(`Display : ${JSON.stringify(this.display.getOutgoingPosition())}`);
}
}
unload() {
this.current = null;
this.info = null;
this.images = [];
}
getState() {
const dimensions = this.display.getOutgoingPosition();
const source = this.display.getSource();
const screen = this.display.getScreen();
return {
display: {
width: dimensions.w,
height: dimensions.h
},
offset: {
x: dimensions.x,
y: dimensions.y
},
screen,
source,
sequence: {
hash: this.current.hash,
name: this.current.name,
progress: this.progress,
current: this.frame,
frames: this.frames,
status: this.getStatus()
},
exposure: this.exposure,
statistics: this.stats !== null ? this.stats.calculate(this.frame) : null
};
}
getUpdateState() {
return {
sequence: {
hash: this.current.hash,
name: this.current.name,
progress: this.progress,
current: this.frame,
frames: this.frames,
status: this.getStatus()
},
exposure: this.exposure
};
}
getSequenceState() {
if (this.current === null) {
return null;
}
return {
hash: this.current.hash,
name: this.current.name,
progress: this.progress
};
}
setExposure(ms) {
this.exposure = ms;
this.log.info(`Updated exposure: ${ms}ms`);
this.updateClientsOnState();
}
getStatus() {
if (this.running && this.paused) {
return SequenceStatus.PAUSED;
}
else if (this.running && !this.paused) {
return SequenceStatus.RUNNING;
}
return SequenceStatus.IDLE;
}
getCurrent() {
if (this.current !== null && this.images.length > 0 && typeof this.images[this.frame] !== 'undefined') {
return this.images[this.frame];
}
return null;
}
frameAdvance(frames = 1) {
if (this.frame + frames >= this.images.length) {
this.frame = this.images.length - 1;
}
else {
this.frame += frames;
}
this.progress = this.frame / (this.images.length - 1);
this.updateClientsOnState();
}
frameRewind(frames = 1) {
if (this.frame - frames < 0) {
this.frame = 0;
}
else {
this.frame -= frames;
}
this.progress = this.frame > 0 ? this.frame / (this.images.length - 1) : 0;
this.updateClientsOnState();
}
frameSet(frame) {
if (frame < 0) {
frame = 0;
}
else if (frame > this.images.length - 1) {
frame = this.images.length - 1;
}
this.frame = frame;
this.progress = this.frame > 0 ? this.frame / (this.images.length - 1) : 0;
this.updateClientsOnState();
}
updateOffset(x, y) {
const current = this.display.getOutgoingPosition();
const screen = this.display.getScreen();
let totalX;
let totalY;
let updated = false;
if (x !== 0) {
if (current.x + x < 0) {
return;
}
totalX = current.x + current.w + x;
if (totalX > screen.width) {
return;
}
this.display.setOffsetX(current.x + x);
updated = true;
}
if (y !== 0) {
if (current.y + y < 0) {
return;
}
totalY = current.y + current.h + y;
if (totalY > screen.height) {
return;
}
this.display.setOffsetY(current.y + y);
updated = true;
}
if (updated) {
this.updateClientsOnDisplay();
}
}
updateSize(width, height) {
const current = this.display.getOutgoingPosition();
const screen = this.display.getScreen();
let totalX;
let totalY;
let updated = false;
if (width !== 0) {
if (current.w + width < 1) {
return;
}
totalX = current.x + current.w + width;
if (totalX > screen.width) {
return;
}
this.display.setWidth(current.w + width);
updated = true;
}
if (height !== 0) {
if (current.h + height < 1) {
return;
}
totalY = current.y + current.h + height;
if (totalY > screen.height) {
return;
}
this.display.setHeight(current.h + height);
updated = true;
}
if (updated) {
this.updateClientsOnDisplay();
}
}
updateScale(scale) {
const current = this.display.getDimensions();
const offset = this.display.getOffset();
const screen = this.display.getScreen();
console.dir(scale);
}
async frameRecord() {
const start = Date.now();
let load;
let open;
let exposureElapsed;
let exposureReported;
let close;
let total;
let result;
const img = this.images[this.frame];
const dimensions = this.display.getOutgoingPosition();
this.log.info(`Frame: ${this.frame} / ${this.images.length}`);
await this.fd.load(img.path, dimensions.x, dimensions.y, dimensions.w, dimensions.h);
load = Date.now() - start;
await this.camera.open();
open = Date.now() - start - load;
result = await this.fd.display(img.path, [this.exposure]);
exposureReported = result.reported;
exposureElapsed = Date.now() - start - load - open;
await this.camera.close();
close = Date.now() - start - load - open - exposureElapsed;
total = Date.now() - start;
this.stats.add(load, open, exposureElapsed, exposureReported, close, total);
}
}
exports.Sequence = Sequence;
//# sourceMappingURL=index.js.map