Summary
In src/hflow/checks.py, _keyframe_interval_value computes max_keyframe_gap_s by appending the stream tail timestamp (stamps_ns[-1]), but omits the stream head (stamps_ns[0]):
https://github.com/Hebbian-Robotics/hflow/blob/main/src/hflow/checks.py#L2255-L2261
When a camera stream starts mid-GOP (first_frame_is_keyframe == 0), the initial span of frames before the first keyframe is never measured, causing max_keyframe_gap_s to severely under-report the maximum seek/decode gap. In extreme cases where the only keyframe occurs at the end of the recording, it reports max_keyframe_gap_s = 0.0.
Problem & Invariant Violation
The documented invariant for keyframe_interval states:
"A keyframe is where a decoder can start, so the longest gap between them bounds both random access and frame-accurate cutting without re-encoding. Measured over true log time rather than the remuxed MP4's synthesized constant-rate clock, so a recording gap legitimately widens the reported gap -- a cut across that gap really does land somewhere else." (checks.py:2276-2280)
The check aims to find the longest span of footage without a keyframe across the stream, which is why it appends stamps_ns[-1] to measure the trailing gap from the last keyframe to the end of the stream.
However, because stamps_ns[0] is omitted:
- If a 10-second recording starts mid-GOP with its first keyframe at
t = 7.0s and subsequent keyframes at t = 8.0s and 10.0s:
- The first 7 seconds have no keyframe — random access and cutting anywhere in
[0.0s, 7.0s] have a 7-second gap.
- But
max_keyframe_gap_s only evaluates gaps between keyframes and the tail (diff([7.0s, 8.0s, 10.0s])), reporting 2.0s instead of 7.0s.
- If a stream has only one keyframe located at the final frame (
t = 5.0s), diff([5.0s, 5.0s]) = [0], returning max_keyframe_gap_s = 0.0s (asserting a perfect zero seek gap when the entire video had no keyframe prior to the end).
- Curation queries filtering on seekable footage (e.g.
SELECT episode_id FROM episodes WHERE "/cam/max_keyframe_gap_s" <= 1.5) silently admit footage with multi-second lead-in dropouts.
Root Cause
In src/hflow/checks.py:2255-2261:
if name == "max_keyframe_gap_s":
if not inter.keyframe_indices:
raise ValueError(f"{name!r}: fact named for a keyframe-less camera")
keyframe_stamps_ns = stamps_ns[list(inter.keyframe_indices)]
gaps_ns = np.diff(np.append(keyframe_stamps_ns, stamps_ns[-1])) # <-- bounds tail, but omits stamps_ns[0]
positive_gaps_ns = gaps_ns[gaps_ns > 0]
return float(np.max(positive_gaps_ns) / 1e9) if len(positive_gaps_ns) else 0.0
Minimal Reproduction
Run this standalone script (mirroring src/hflow/checks.py:2255-2261):
import numpy as np
# Camera stream lasting 5.0 seconds (t=0.0s to 5.0s)
stamps_ns = np.array([0, 1_000_000_000, 2_000_000_000, 3_000_000_000, 4_000_000_000, 5_000_000_000])
# Scenario A: Camera starts mid-GOP; first keyframe arrives at t=4.0s, stream ends at t=5.0s
keyframe_indices = [4]
# Current hflow implementation (checks.py:2258-2261):
keyframe_stamps_ns = stamps_ns[list(keyframe_indices)]
gaps_ns = np.diff(np.append(keyframe_stamps_ns, stamps_ns[-1]))
positive_gaps_ns = gaps_ns[gaps_ns > 0]
reported = float(np.max(positive_gaps_ns) / 1e9) if len(positive_gaps_ns) else 0.0
print(f"Scenario A - Reported: {reported:.1f}s | Expected: 4.0s (gap from t=0.0s to t=4.0s)")
# Scenario B: Extreme case where the only keyframe is the final frame (t=5.0s)
keyframe_stamps_ns_b = stamps_ns[[5]]
gaps_ns_b = np.diff(np.append(keyframe_stamps_ns_b, stamps_ns[-1]))
positive_gaps_ns_b = gaps_ns_b[gaps_ns_b > 0]
reported_b = float(np.max(positive_gaps_ns_b) / 1e9) if len(positive_gaps_ns_b) else 0.0
print(f"Scenario B - Reported: {reported_b:.1f}s | Expected: 5.0s (gap from t=0.0s to t=5.0s)")
Summary
In
src/hflow/checks.py,_keyframe_interval_valuecomputesmax_keyframe_gap_sby appending the stream tail timestamp (stamps_ns[-1]), but omits the stream head (stamps_ns[0]):https://github.com/Hebbian-Robotics/hflow/blob/main/src/hflow/checks.py#L2255-L2261
When a camera stream starts mid-GOP (
first_frame_is_keyframe == 0), the initial span of frames before the first keyframe is never measured, causingmax_keyframe_gap_sto severely under-report the maximum seek/decode gap. In extreme cases where the only keyframe occurs at the end of the recording, it reportsmax_keyframe_gap_s = 0.0.Problem & Invariant Violation
The documented invariant for
keyframe_intervalstates:The check aims to find the longest span of footage without a keyframe across the stream, which is why it appends
stamps_ns[-1]to measure the trailing gap from the last keyframe to the end of the stream.However, because
stamps_ns[0]is omitted:t = 7.0sand subsequent keyframes att = 8.0sand10.0s:[0.0s, 7.0s]have a 7-second gap.max_keyframe_gap_sonly evaluates gaps between keyframes and the tail (diff([7.0s, 8.0s, 10.0s])), reporting2.0sinstead of7.0s.t = 5.0s),diff([5.0s, 5.0s]) = [0], returningmax_keyframe_gap_s = 0.0s(asserting a perfect zero seek gap when the entire video had no keyframe prior to the end).SELECT episode_id FROM episodes WHERE "/cam/max_keyframe_gap_s" <= 1.5) silently admit footage with multi-second lead-in dropouts.Root Cause
In
src/hflow/checks.py:2255-2261:Minimal Reproduction
Run this standalone script (mirroring
src/hflow/checks.py:2255-2261):