Say when a recording starts and when it cannot keep up
A 4K60 game view recording on the user's machine had 517 of 827 frames. The rest were skipped without a word: when the editor's tick came late, FrameGate let the due slots pass, and only frames lost to a busy encoder or read back counted as dropped. The file kept its time but stuttered, and nothing said so. - FrameGate counts the frames it took and the slots the recording's time has room for. The difference is skipped, next to dropped in __gm.recording(). - FramePace judges the missed frames over spans of 2 s. The first time more than 10 % are missed, the log warns once with the frames a second the recording really got. - At the end, when more than 5 % are missed, a warning gives the frames of the whole file and what helps (30 fps or a smaller resolution). It comes after the upload's notice, which would cover it at once in the editor. - The first second counts for neither warning. Without that, a short e2e recording warned about 3 frames lost while starting. - A recording that starts logs an info line with its size, fps, codec and sound. TakeHost.log takes a level for it. The editor recording keeps the line to the console, its notice would be in the video. - consoleLine is the one place with an eslint exception for console.info, so the console panel shows the line plain. Found along the way: headless Chrome in WSL records the editor with real stutter in e2e 67 (1280x800@60 27 %, 2304x1440@30 12-14 %). That is the software encoder there, and only now shown. Tests: - Gate on melektaus-atx: green, 309 files, 4810 tests. - Unit recording.test.ts: 36 green, 6 new for FrameGate.skipped and FramePace. With skipped stuck at 0 the test turns red. - e2e 67 on melektaus-atx: ALL PASSED, 15 checks, run twice. The new 67b-ii slows the editor to about 20 fps: a 640x360@60 recording says "about 19 of 60 fps" and saves with 95 of 192 frames. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
4d0a696327
commit
77365f17d3
9 files changed
+283
-17
No files matched your search
@@ -120,7 +120,60 @@ export async function recording(t) {
|
||||
log.some((line) => line.includes('Start recording is ignored') && line.includes(during.takes[1].name)),
|
||||
JSON.stringify({ log, takes: during.takes.map((take) => take.name) }),
|
||||
);
|
||||
check(
|
||||
'a recording says in the log when it starts: its size, fps and sound',
|
||||
log.some((line) => line === `Recording "${NAME}" started: 1920x1080 at 30 fps, H.264, with sound`) &&
|
||||
log.some(
|
||||
(line) =>
|
||||
line.startsWith(`Recording "${during.takes[1].name}" started: `) && line.includes(' at 60 fps'),
|
||||
),
|
||||
JSON.stringify(log),
|
||||
);
|
||||
|
||||
// 67b-ii. Frames 45 ms apart, about 22 a second: the second take, at 60 fps, skips most of its
|
||||
// frames. It says so once while it records, and again when it is saved, with the count.
|
||||
await evaluate(`(() => {
|
||||
window.__rc67slow = true;
|
||||
const slow = () => {
|
||||
if (!window.__rc67slow) return;
|
||||
const until = performance.now() + 45;
|
||||
while (performance.now() < until);
|
||||
requestAnimationFrame(slow);
|
||||
};
|
||||
requestAnimationFrame(slow);
|
||||
})()`);
|
||||
const behind = `(__gm.world()?.log ?? []).find((line) => line.includes("can't keep up"))`;
|
||||
await waitUntil(`${behind} !== undefined`, {
|
||||
timeout: 15_000,
|
||||
label: 'the slow take says it falls behind',
|
||||
});
|
||||
await evaluate('window.__rc67slow = false; 0');
|
||||
const behindLine = await json(behind);
|
||||
const slowTake = (await recordings()).takes[1];
|
||||
const got = Number(/about (\d+) of 60 fps/.exec(behindLine ?? '')?.[1]);
|
||||
check(
|
||||
'a recording that falls behind says so while it records, with the frames a second it gets',
|
||||
behindLine?.startsWith(`Recording "${slowTake.name}" can't keep up`) &&
|
||||
got > 5 &&
|
||||
got < 40 &&
|
||||
slowTake.skipped > 0 &&
|
||||
slowTake.dropped + slowTake.skipped > slowTake.frames / 2,
|
||||
JSON.stringify({ behindLine, slowTake }),
|
||||
);
|
||||
await stop();
|
||||
await waitUntil(`__gm.recording().takes[1]?.state === 'done'`, { label: 'the slow take is saved' });
|
||||
const consoleLines = (await json('__gm.console.lines()')).filter((line) =>
|
||||
line.text.startsWith('[recording]'),
|
||||
);
|
||||
const summary = consoleLines.find((line) => line.text.includes(`"${slowTake.name}" has `));
|
||||
check(
|
||||
'and says when it is saved how many frames it missed; the start is an info line, the rest warns',
|
||||
summary?.level === 'warn' &&
|
||||
/has \d+ of \d+ frames: \d+ missed \(\d+ %\), so it stutters/.test(summary.text) &&
|
||||
consoleLines.some((line) => line.level === 'info' && line.text.includes(`"${NAME}" started`)) &&
|
||||
consoleLines.some((line) => line.level === 'warn' && line.text.includes("can't keep up")),
|
||||
JSON.stringify(consoleLines.slice(-8)),
|
||||
);
|
||||
|
||||
// 67c. Ending the run saved the second take; both became video assets once the server had them.
|
||||
await waitUntil(
|
||||
|
||||
Reference in new issue
Block a user