fix(engine): add epsilon to frame index floor to prevent IEEE 754 boundary duplicates (#1318)

## Summary

Fixes #1317 — systematic duplicate+skip video frames when clip `data-start` is aligned to the output frame grid.

### Root cause

`Math.floor(localTime * fps)` in `getFrameAtTime` produces off-by-one errors when the product lands exactly on an integer boundary due to IEEE 754 float noise. For example, `0.28 * 25 === 6.999999999999999` instead of `7`, causing `Math.floor` to return 6 (duplicate of previous frame) instead of 7.

### Fix

1. Add `1e-9` epsilon before flooring: `Math.floor(localTime * fps + 1e-9)` — nudges boundary values like `6.999999` to `7.000000` without affecting mid-frame values.
2. Include `mediaStart` in the frame index computation so trimmed clips (`data-media-start`) map to the correct extracted frames.

Both call sites fixed: `getFrameAtTime()` (public API) and the `FrameLookupTable.getFramesAtTime()` bulk lookup.

### Reporter's measurements (before fix)

| Case | Duplicates (of 351 frames) |
|---|---|
| Source file | 1 |
| data-start="0" | 14 |
| data-start="230.44" (production) | 127 |
| data-start="0.02" (half-frame offset workaround) | 1 |

## Test plan

- [x] 4 new regression tests for IEEE 754 boundary precision
- [x] No duplicate frames when data-start is grid-aligned (25fps)
- [x] Monotonically increasing frame indices across 100 frames
- [x] Correct frame at the `0.28 * 25` boundary (frame 7, not 6)
- [x] `mediaStart` correctly offsets frame index
- [x] Typecheck clean
This commit is contained in:
Miguel Ángel
2026-06-10 16:37:03 -04:00
committed by GitHub
parent 8133d9346e
commit 3a72aa528d
3 changed files with 63 additions and 3 deletions
@@ -1 +0,0 @@
{"version":"4.1.4","results":[[":src/utils/paths.test.ts",{"duration":10.026583000000016,"failed":false}],[":src/services/renderOrchestrator.test.ts",{"duration":12.886541999999963,"failed":false}],[":src/services/htmlCompiler.test.ts",{"duration":0,"failed":true}],[":src/services/hyperframeRuntimeLoader.test.ts",{"duration":27.842709000000013,"failed":false}]]}