Summary
analyzeKeyframeIntervals bails out when a video has fewer than two keyframes and reports isProblematic: false. But a video with exactly one keyframe is the worst case for the very failure mode the warning exists to catch: every seek past 0 lands inside a single GOP that spans the whole file. The compiler warns about a 5-second GOP and stays silent about a 10-second one.
In analyzeKeyframeIntervalsUncached (bundled from engine/src/utils/ffprobe.ts):
const timestamps = stdout.split("\n").map(l => parseFloat(l.trim())).filter(Number.isFinite);
if (timestamps.length < 2) {
return { avgIntervalSeconds: 0, maxIntervalSeconds: 0, keyframeCount: timestamps.length, isProblematic: false };
}
The early return is reasonable for still images and 1-frame assets, but it also swallows real single-GOP videos of arbitrary length.
Reproduction
Two 10-second clips, identical except for GOP size:
ffmpeg -y -f lavfi -i testsrc=size=640x360:rate=30:duration=10 \
-c:v libx264 -pix_fmt yuv420p -g 1000 -keyint_min 1000 -sc_threshold 0 assets/single-gop.mp4
ffmpeg -y -f lavfi -i testsrc=size=640x360:rate=30:duration=10 \
-c:v libx264 -pix_fmt yuv420p -g 150 -keyint_min 150 -sc_threshold 0 assets/five-second-gop.mp4
Keyframe timestamps:
single-gop.mp4 0.000000
five-second-gop.mp4 0.000000 5.000000
index.html — both clips, each seeking to 8s:
<div id="root" data-composition-id="gop-repro" data-start="0" data-duration="2" data-width="640" data-height="360">
<video id="single-gop" class="clip layer" data-start="0" data-duration="1" data-track-index="0" data-media-start="8" src="assets/single-gop.mp4" muted></video>
<video id="five-second-gop" class="clip layer" data-start="1" data-duration="1" data-track-index="0" data-media-start="8" src="assets/five-second-gop.mp4" muted></video>
</div>
npx hyperframes@0.8.10 render . -o out.mp4 2>&1 | grep Compiler
Observed
[WARN] [Compiler] WARNING: Video "five-second-gop" has sparse keyframes (max interval: 5s). ...
Nothing for single-gop, whose effective seek distance is 10s — twice as bad.
Expected
single-gop should warn at least as loudly as five-second-gop.
Suggested fix
When exactly one keyframe is found, the effective interval is the stream duration, not zero:
if (timestamps.length === 1) {
const duration = await probeDurationSeconds(filePath); // already available via extractVideoMetadata
return {
avgIntervalSeconds: duration,
maxIntervalSeconds: duration,
keyframeCount: 1,
isProblematic: duration > 2,
};
}
if (timestamps.length === 0) {
return { avgIntervalSeconds: 0, maxIntervalSeconds: 0, keyframeCount: 0, isProblematic: false };
}
Still images and single-frame assets keep their current behaviour, since their duration is 0 or below the threshold.
Why it matters in practice
This is not a synthetic corner case. In a 414-clip library downloaded straight from Pexels and Pixabay, 26 files are single-GOP with durations from 2.3s to 10s — exactly the ones the check skips. Overall that library sits well above the 2s threshold anyway (median max interval 3.04s, 82% above 3s), so in practice the check fires on nearly everything except the worst offenders.
Side note, offered as data rather than a complaint
While measuring the above we could not reproduce the failure the warning describes, on hyperframes render at least. Two stands cut deliberately mid-GOP — a stock clip with keyframes 10s apart, and an Archive.org master with 5.51s intervals, cut 3.5s past a keyframe — both rendered frame-accurate: mean per-pixel luma delta against ffmpeg-extracted ground truth was 1.4–1.8, versus 15–60 against any neighbouring moment. The video_extract phase resolves frames through ffmpeg (minVideoFrameCoverageRatio: 1), which decodes from the preceding keyframe correctly. Re-encoding per the suggested command changed render wall-clock by ~3% (95s vs 98s on the same stand), i.e. noise.
So the warning may be aimed at a different path (browser seeking in preview/studio?) than the one render takes. If that is the case, narrowing its wording or its scope might be worth considering alongside the fix above.
Environment: macOS 26.5.2 (arm64), Node 26.0.0, hyperframes 0.8.10 (also verified on 0.7.106).
Summary
analyzeKeyframeIntervalsbails out when a video has fewer than two keyframes and reportsisProblematic: false. But a video with exactly one keyframe is the worst case for the very failure mode the warning exists to catch: every seek past 0 lands inside a single GOP that spans the whole file. The compiler warns about a 5-second GOP and stays silent about a 10-second one.In
analyzeKeyframeIntervalsUncached(bundled fromengine/src/utils/ffprobe.ts):The early return is reasonable for still images and 1-frame assets, but it also swallows real single-GOP videos of arbitrary length.
Reproduction
Two 10-second clips, identical except for GOP size:
Keyframe timestamps:
index.html— both clips, each seeking to 8s:Observed
Nothing for
single-gop, whose effective seek distance is 10s — twice as bad.Expected
single-gopshould warn at least as loudly asfive-second-gop.Suggested fix
When exactly one keyframe is found, the effective interval is the stream duration, not zero:
Still images and single-frame assets keep their current behaviour, since their duration is 0 or below the threshold.
Why it matters in practice
This is not a synthetic corner case. In a 414-clip library downloaded straight from Pexels and Pixabay, 26 files are single-GOP with durations from 2.3s to 10s — exactly the ones the check skips. Overall that library sits well above the 2s threshold anyway (median max interval 3.04s, 82% above 3s), so in practice the check fires on nearly everything except the worst offenders.
Side note, offered as data rather than a complaint
While measuring the above we could not reproduce the failure the warning describes, on
hyperframes renderat least. Two stands cut deliberately mid-GOP — a stock clip with keyframes 10s apart, and an Archive.org master with 5.51s intervals, cut 3.5s past a keyframe — both rendered frame-accurate: mean per-pixel luma delta against ffmpeg-extracted ground truth was 1.4–1.8, versus 15–60 against any neighbouring moment. Thevideo_extractphase resolves frames through ffmpeg (minVideoFrameCoverageRatio: 1), which decodes from the preceding keyframe correctly. Re-encoding per the suggested command changed render wall-clock by ~3% (95s vs 98s on the same stand), i.e. noise.So the warning may be aimed at a different path (browser seeking in preview/studio?) than the one
rendertakes. If that is the case, narrowing its wording or its scope might be worth considering alongside the fix above.Environment: macOS 26.5.2 (arm64), Node 26.0.0, hyperframes 0.8.10 (also verified on 0.7.106).