51 lines
1.7 KiB
TypeScript
51 lines
1.7 KiB
TypeScript
import { execSync } from 'node:child_process';
|
|
import { renameSync, statSync } from 'node:fs';
|
|
import { MAX_DURATION_S, OUTPUT_FPS, VIDEO_SIZE, WEBM_BITRATE } from './config.js';
|
|
|
|
const LEAD_IN_S = 0.12;
|
|
|
|
export function trimRecording(
|
|
rawPath: string,
|
|
trimmedPath: string,
|
|
times: { recordStartMs: number; sceneStartMs: number; sceneEndMs: number }
|
|
) {
|
|
const sceneSpan = (times.sceneEndMs - times.sceneStartMs) / 1000;
|
|
const trimStart = Math.max(
|
|
0,
|
|
(times.sceneStartMs - times.recordStartMs) / 1000 - LEAD_IN_S
|
|
);
|
|
const trimEnd = (times.sceneEndMs - times.recordStartMs) / 1000;
|
|
const wallDuration = trimEnd - trimStart;
|
|
const finalDuration = wallDuration;
|
|
|
|
if (finalDuration > MAX_DURATION_S) {
|
|
console.log(
|
|
`Scene output duration is ${finalDuration.toFixed(2)}s (guard ${MAX_DURATION_S.toFixed(2)}s); keeping the full take.`
|
|
);
|
|
}
|
|
|
|
const filter =
|
|
`trim=start=${trimStart.toFixed(3)}:duration=${wallDuration.toFixed(3)},` +
|
|
`setpts=PTS-STARTPTS,fps=${OUTPUT_FPS},` +
|
|
`trim=duration=${finalDuration.toFixed(3)},setpts=PTS-STARTPTS`;
|
|
|
|
// Keep trimming inside the filter graph: it is frame-accurate for WebM
|
|
// without the keyframe leakage of input seeking.
|
|
execSync(
|
|
`ffmpeg -y -i "${rawPath}" -vf "${filter}" ` +
|
|
`-fps_mode cfr -r ${OUTPUT_FPS} -c:v libvpx -b:v ${WEBM_BITRATE} -deadline good -cpu-used 5 ` +
|
|
`"${trimmedPath}"`,
|
|
{ stdio: 'inherit' }
|
|
);
|
|
|
|
try {
|
|
statSync(rawPath);
|
|
renameSync(rawPath, rawPath + '.untrimmed');
|
|
} catch {
|
|
/* ignore */
|
|
}
|
|
|
|
console.log(
|
|
`Wrote ${trimmedPath} (${finalDuration.toFixed(2)}s, scene=${sceneSpan.toFixed(2)}s, capture=${VIDEO_SIZE.width}x${VIDEO_SIZE.height})`
|
|
);
|
|
}
|