mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-10 22:20:14 +00:00
perf(engine): segment-scope SDR→HDR preflight (#445)
## What Scopes the SDR→HDR preflight re-encode to the segment the composition actually uses, mirroring the existing VFR→CFR segment-scope fix. ## Why `convertSdrToHdr` was re-encoding entire source files, so a 30-minute SDR screen recording contributing a 2-second clip in a mixed HDR/SDR composition ate multi-second preflight time that produced frames no one would ever read. Validated on a mixed 30s-SDR + 2s-HDR fixture: `hdrPreflightMs` drops **87%** (1162→148ms), `videoExtractMs` drops **82%** (1272→231ms), `tmpPeakBytes` drops **45%** (8.2MB→4.5MB). Depends on #444 (phase-level instrumentation) for the measurement surface. ## How - `convertSdrToHdr` gains `startTime` and `duration` parameters ahead of the upstream `targetTransfer` arg added by #370. New signature: `convertSdrToHdr(input, output, startTime, duration, targetTransfer, signal, config)`. `-ss $start -t $duration` is added to the ffmpeg args. - Phase 2 now captures the full `VideoMetadata` per `resolvedVideos` entry (previously just `colorSpace`) so the caller can compute `segDuration` from `video.end - video.start` with a fallback to `metadata.durationSeconds - video.mediaStart` for unbounded (Infinity) clips — without firing another ffprobe. - After a successful convert, `entry.video.mediaStart` is zeroed out via shallow-copy (doesn't mutate the caller's `VideoElement`) so downstream extraction seeks from 0 instead of the original offset. Mirrors what the VFR→CFR path already does. ## Test plan Validation on `/tmp/hf-fixtures/hdr-sdr-mixed-scope`: ``` hdrPreflightMs: >1000 → 150 (gate: <300) ✓ videoExtractMs: 1272 → 237 (-82%) tmpPeakBytes: 8.2MB → 4.5MB (-45%) ``` - [x] Unit test: new regression test synthesizes 10s SDR + 2s HDR fixture inline and asserts the converted file's duration matches the 2s used segment (pre-fix matched the 10s source) - [x] Lint + format - [x] Typecheck - [x] Manual perf validation against synthesized fixture
This commit is contained in:
@@ -196,6 +196,87 @@ describe.skipIf(!HAS_FFMPEG)("extractAllVideoFrames on a VFR source", () => {
|
||||
expect(result.phaseBreakdown.vfrPreflightMs).toBeGreaterThan(0);
|
||||
}, 60_000);
|
||||
|
||||
// Regression test for the segment-scope HDR preflight fix: pre-fix,
|
||||
// convertSdrToHdr re-encoded the entire source, so a 30-minute SDR source
|
||||
// contributing a 2-second clip took ~200× longer than needed. Post-fix the
|
||||
// converted file's duration matches the used segment.
|
||||
it("bounds the SDR→HDR preflight re-encode to the used segment", async () => {
|
||||
const SDR_LONG = join(FIXTURE_DIR, "sdr-long.mp4");
|
||||
const HDR_SHORT = join(FIXTURE_DIR, "hdr-short.mp4");
|
||||
|
||||
const sdrResult = await runFfmpeg([
|
||||
"-y",
|
||||
"-hide_banner",
|
||||
"-loglevel",
|
||||
"error",
|
||||
"-f",
|
||||
"lavfi",
|
||||
"-i",
|
||||
"testsrc2=s=320x180:d=10:rate=30",
|
||||
"-c:v",
|
||||
"libx264",
|
||||
"-preset",
|
||||
"ultrafast",
|
||||
"-pix_fmt",
|
||||
"yuv420p",
|
||||
SDR_LONG,
|
||||
]);
|
||||
if (!sdrResult.success) {
|
||||
throw new Error(`SDR fixture synthesis failed: ${sdrResult.stderr.slice(-400)}`);
|
||||
}
|
||||
|
||||
// Tag as bt2020nc / smpte2084 so the preflight path considers the timeline mixed-HDR.
|
||||
const hdrResult = await runFfmpeg([
|
||||
"-y",
|
||||
"-hide_banner",
|
||||
"-loglevel",
|
||||
"error",
|
||||
"-f",
|
||||
"lavfi",
|
||||
"-i",
|
||||
"testsrc2=s=320x180:d=2:rate=30",
|
||||
"-c:v",
|
||||
"libx264",
|
||||
"-preset",
|
||||
"ultrafast",
|
||||
"-pix_fmt",
|
||||
"yuv420p",
|
||||
"-color_primaries",
|
||||
"bt2020",
|
||||
"-color_trc",
|
||||
"smpte2084",
|
||||
"-colorspace",
|
||||
"bt2020nc",
|
||||
HDR_SHORT,
|
||||
]);
|
||||
if (!hdrResult.success) {
|
||||
throw new Error(`HDR fixture synthesis failed: ${hdrResult.stderr.slice(-400)}`);
|
||||
}
|
||||
|
||||
const outputDir = join(FIXTURE_DIR, "out-hdr-segment");
|
||||
mkdirSync(outputDir, { recursive: true });
|
||||
|
||||
const videos: VideoElement[] = [
|
||||
{ id: "sdr", src: SDR_LONG, start: 0, end: 2, mediaStart: 0, hasAudio: false },
|
||||
{ id: "hdr", src: HDR_SHORT, start: 2, end: 4, mediaStart: 0, hasAudio: false },
|
||||
];
|
||||
|
||||
const result = await extractAllVideoFrames(videos, FIXTURE_DIR, {
|
||||
fps: 30,
|
||||
outputDir,
|
||||
});
|
||||
expect(result.errors).toEqual([]);
|
||||
expect(result.phaseBreakdown.hdrPreflightCount).toBe(1);
|
||||
|
||||
const convertedPath = join(outputDir, "_hdr_normalized", "sdr_hdr.mp4");
|
||||
expect(existsSync(convertedPath)).toBe(true);
|
||||
const convertedMeta = await extractVideoMetadata(convertedPath);
|
||||
// Pre-fix duration matched the 10s source; post-fix it matches the 2s segment
|
||||
// (±0.2s for encoder keyframe/seek alignment).
|
||||
expect(convertedMeta.durationSeconds).toBeGreaterThan(1.8);
|
||||
expect(convertedMeta.durationSeconds).toBeLessThan(2.5);
|
||||
}, 60_000);
|
||||
|
||||
// Asserts both frame-count correctness and that we don't emit long runs of
|
||||
// byte-identical "duplicate" frames — the user-visible "frozen screen
|
||||
// recording" symptom. Pre-fix duplicate rate on this fixture is ~38%
|
||||
|
||||
Reference in New Issue
Block a user