Skip to content

Commit cdaeb80

Browse files
committed
feat(server): continue capturing while cropping and saving
instead of waiting for those to finish
1 parent e9b0206 commit cdaeb80

File tree

3 files changed

+64
-32
lines changed

3 files changed

+64
-32
lines changed

client/components/Notifications.jsx

+9-3
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,17 @@ function Notifications() {
1212
// projector
1313
useCommsNotificationValue(
1414
'captureStats',
15-
({ stats: { frameCount, started, now } }) => {
15+
({
16+
stats: {
17+
capturedFrameCount, savedFrameCount, started, now,
18+
},
19+
}) => {
1620
const secondsElapsed = Math.round((now - started) / 1e3);
1721
// allNotifications reference here is only the first value
18-
// TODO: refactor this clumbsy approach (in every usage)
19-
allNotifications.captureStats = `${frameCount} frames in ${secondsElapsed}s, or ${friendlyRound(frameCount / secondsElapsed)} fps (${friendlyRound(secondsElapsed / frameCount)} spf)`;
22+
// TODO: refactor this clumsy approach (in every usage)
23+
allNotifications.captureStats = ''
24+
+ `captured ${capturedFrameCount} frames in ${secondsElapsed}s, or ${friendlyRound(capturedFrameCount / secondsElapsed)} fps (${friendlyRound(secondsElapsed / capturedFrameCount)} spf)\n`
25+
+ `saved ${savedFrameCount} frames in ${secondsElapsed}s, or ${friendlyRound(savedFrameCount / secondsElapsed)} fps (${friendlyRound(secondsElapsed / savedFrameCount)} spf)`;
2026
setNotifications({ ...allNotifications });
2127
},
2228
null

server/camera.js

+14-12
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ function takePhoto() {
129129
height,
130130
},
131131
};
132-
setTimeout(() => cameraEvents.emit('frame', latestFrame), 1007);
132+
setTimeout(() => cameraEvents.emit('frame', latestFrame), 100);
133133
return latestFrame;
134134
});
135135

@@ -142,17 +142,19 @@ function captureFrame() {
142142
.then(({ photo, encoding }) => {
143143
console.timeEnd('captureFrame call of takePhoto');
144144
console.time('extract & toBuffer');
145-
return sharp(photo)
146-
.extract(cropWindow)
147-
.toBuffer()
148-
.then((croppedPhoto) => {
149-
console.timeEnd('extract & toBuffer');
150-
return {
151-
photo: croppedPhoto,
152-
encoding,
153-
size: cropWindow,
154-
};
155-
});
145+
return {
146+
cropPromise: sharp(photo)
147+
.extract(cropWindow)
148+
.toBuffer()
149+
.then((croppedPhoto) => {
150+
console.timeEnd('extract & toBuffer');
151+
return {
152+
photo: croppedPhoto,
153+
encoding,
154+
size: cropWindow,
155+
};
156+
}),
157+
};
156158
});
157159
}
158160

server/projector.js

+41-17
Original file line numberDiff line numberDiff line change
@@ -135,17 +135,20 @@ function captureFrame(frameIdentifier, folderName = '', skipBusyIdleNotification
135135
// but we should return the fs op chain to avoid keeping too much data in memory
136136
// (too many photos build up due to fs being slower than the stepper motor)
137137

138-
const getFrameChain = currentOperation
138+
const getFramePromise = currentOperation
139139
.then(() => emitBusy(skipBusyIdleNotifications))
140140
.then(() => {
141141
console.time('camera captureFrame');
142142
return camera.captureFrame();
143143
});
144144

145-
currentOperation = getFrameChain
145+
const getCroppedFramePromise = getFramePromise
146+
.then(({ cropPromise }) => cropPromise);
147+
148+
currentOperation = getFramePromise
146149
.then(() => emitIdle(skipBusyIdleNotifications));
147150

148-
return getFrameChain.then(({ photo, encoding, size }) => {
151+
const saveFramePromise = getCroppedFramePromise.then(({ photo, encoding, size }) => {
149152
console.timeEnd('camera captureFrame');
150153
// save photo to filesystem
151154
const filename = `frame-${frameIdentifier || Date.now()}.${encoding}`;
@@ -171,38 +174,59 @@ function captureFrame(frameIdentifier, folderName = '', skipBusyIdleNotification
171174
return filePath;
172175
});
173176
});
177+
178+
return { getFramePromise, getCroppedFramePromise, saveFramePromise };
174179
}
175180

176181
function captureAndAdvance(
177182
folderName = `capture-group-${Date.now()}`,
178183
frameNumber = 0,
179-
stats = { frameCount: 0, started: Date.now() }
184+
stats = { capturedFrameCount: 0, savedFrameCount: 0, started: Date.now() }
180185
) {
181186
// avoid emitting every capture, advance
182-
if (stats.frameCount === 0) {
187+
if (stats.capturedFrameCount === 0) {
183188
emitBusy();
184189
needToStopCaptureAndAdvance = false;
185190
camera.pausePeriodicCaptures();
186191
}
187192

188193
console.time('projector captureFrame');
189-
captureFrame(`${frameNumber}`, folderName, true)
194+
const { getFramePromise, saveFramePromise } = captureFrame(`${frameNumber}`, folderName, true);
195+
196+
const getFrame = getFramePromise
197+
.then(() => advanceFrame(true))
190198
.then(() => {
191-
console.timeEnd('projector captureFrame');
192-
return advanceFrame(true);
193-
})
199+
// stats could be updated in multiple Promise branches, need the same instance
200+
// eslint-disable-next-line no-param-reassign
201+
stats.capturedFrameCount += 1;
202+
const now = Date.now();
203+
projectorEvents.emit('captureStats', { ...stats, now });
204+
const secondsElapsed = Math.round((now - stats.started) / 1e3);
205+
console.log(`captured ${stats.capturedFrameCount} in ${secondsElapsed}s, or ${stats.capturedFrameCount / secondsElapsed} fps (${secondsElapsed / stats.capturedFrameCount} spf)`);
206+
});
207+
208+
const saveFrame = saveFramePromise
194209
.then(() => {
195-
const presentStats = { ...stats };
196-
presentStats.frameCount += 1;
210+
// stats could be updated in multiple Promise branches, need the same instance
211+
// eslint-disable-next-line no-param-reassign
212+
stats.savedFrameCount += 1;
197213
const now = Date.now();
198-
projectorEvents.emit('captureStats', { ...presentStats, now });
199-
const secondsElapsed = Math.round((now - presentStats.started) / 1e3);
200-
console.log(`${presentStats.frameCount} in ${secondsElapsed}s, or ${presentStats.frameCount / secondsElapsed} fps (${secondsElapsed / presentStats.frameCount} spf)`);
214+
projectorEvents.emit('captureStats', { ...stats, now });
215+
const secondsElapsed = Math.round((now - stats.started) / 1e3);
216+
console.log(`saved ${stats.savedFrameCount} in ${secondsElapsed}s, or ${stats.savedFrameCount / secondsElapsed} fps (${secondsElapsed / stats.savedFrameCount} spf)`);
217+
});
218+
219+
// FIXME: wait for these filesystem operations to finish if they're taking too long
220+
// currently, they're 1-3ms total, while taking a picture and cropping take around 700ms each
221+
// so fs ops are not the bottleneck
222+
const needToWaitForSaves = false;
223+
224+
return (needToWaitForSaves ? saveFrame : getFrame)
225+
.then(() => {
201226
if (needToStopCaptureAndAdvance) {
202-
presentStats.finished = now;
203-
return presentStats;
227+
return stats;
204228
}
205-
return captureAndAdvance(folderName, frameNumber + 1, presentStats);
229+
return captureAndAdvance(folderName, frameNumber + 1, stats);
206230
});
207231
}
208232

0 commit comments

Comments
 (0)