Skip to content

fix(engine): hold finite video slots past EOF - #3962

Merged
miga-heygen merged 1 commit into
mainfrom
bugfix/1787766784-media-start-eof
Sep 15, 2026
Merged

miga-heygen merged 1 commit into
mainfrom
bugfix/1787766784-media-start-eof

Conversation

@heygengenesis

@heygengenesis heygengenesis Bot commented Sep 15, 2026

Copy link
Copy Markdown
Contributor

Fixes #3958.

Summary

  • Route explicit finite, non-looping video slots with data-media-start at or beyond playable EOF through the existing held-final-frame extraction path.
  • Preserve the out-of-range rejection for source-bounded and looping clips.
  • Add the warning-only video_media_start_at_or_past_eof project-lint finding for probeable local slots, and additive extraction-window and decoded-frame coverage.

Validation

  • bun run --cwd packages/engine typecheck
  • bun run --cwd packages/lint typecheck
  • bunx vitest run src/services/videoFrameExtractor.test.ts -t 'resolveVideoExtractionDuration|renders the same final decoded frame just inside and past EOF' — 30 passed
  • bunx vitest run src/project.test.ts — 37 passed
  • bunx oxlint and bunx oxfmt --check on the five changed files
  • bunx fallow audit --base origin/main --fail-on-issues — no new issues in the changed files

The wider engine test file has seven pre-existing VFR integration failures on this host because FFmpeg 4.4 does not support its existing -fps_mode option; the new CFR held-frame integration case passes on the same host.

Evidence

Commit-pinned references

The main-branch rejection is at the early planner guard and the HDR preflight guard.

The changed head code is held-tail eligibility and planner routing, HDR preflight parity, extraction-window tests, decoded-frame integration test, project-lint aggregation, warning invocation, warning rule, and warning tests.

Allow explicit finite non-looping video slots to use the existing held-final-frame extraction path when data-media-start is at or beyond source duration. Warn during project lint for local static slots that will use this behavior.

Co-authored-by: miguel.sierra <229591595+miguel-heygen@users.noreply.github.com>

@miga-heygen miga-heygen left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed head 59041d4 in an isolated worktree. Engine videoFrameExtractor.test.ts 158/159 here (the one failure, "unindexed negative-base MPEG-TS", fails identically on the base commit under this host's static ffprobe, so it is environmental); lint project.test.ts 37/37.

Strengths

  • videoFrameExtractor.ts:1018-1021 — one predicate, canHoldFinalFramePastEof, decides eligibility at all three sites (:1126, :1208-1209, :1755), so loop and open-ended slots cannot drift apart between the planner and the guards.
  • :1126-1137 — the new branch sits after the sourceRemaining > 0 held-tail branch and only reaches when sourceRemaining is 0, so every window with mediaStart < playableDuration takes exactly the code it took before. Disabling the branch fails the new unit test and the decoded-frame integration test; nothing else moves.
  • :1174-1178Math.max(sourceRemaining, window.durationSeconds) is what makes the async resolver safe for a negative sourceRemaining; the emitted window still uses the probed extractionMediaStart: finalFrameTimestamp and finalFrameOnly: true, so the held frame is the real last decoded frame and the cache/dedupe identity (:1860 "final-frame" transform, :2086 key) is the same one the just-inside-EOF path already uses. Sharing that entry is correct: both slots want the same frame.
  • Guard mutations: dropping !video.loop fails "rejects a looping explicit slot that starts at source EOF"; dropping Number.isFinite(timelineDuration) fails both pre-existing "rejects a media start at … EOF" tests (their video() fixture is open-ended), which also confirms the open-ended slot still errors as before. Flipping the lint predicate to <= fails the mediaStart === duration case; changing severity to error fails both warning tests. git diff --numstat shows the two test files are additions only (+70/0, +104/0).

Important (test coverage; the code is right by reading, so not blocking)

  • videoFrameExtractor.ts:1755 — the SDR-in-HDR-timeline preflight guard now lets held-past-EOF slots through, but reverting it to the old mediaStart >= playableDuration leaves the whole suite green. The integration test is all-SDR, so this branch (:1732 hdrInfo.hasHdr && dominantTransfer) never runs. Without a mixed SDR/HDR fixture the render-side fix is unpinned on exactly the path where the old guard used to reject.
  • project.test.ts:654-666 — the "silently skips open, looping, remote, variable, missing, and unprobeable slots" case mocks ffprobe to fail for every file, so all six videos are dropped by the failed probe, not by their exclusion. Removing the loop exclusion at hevcPreviewLint.ts:184 keeps the test green; the same holds for the open-slot and data-var-src exclusions since they share the fixture. Give that test a succeeding probe (duration 2) and assert []; then each exclusion is what suppresses the warning. Remote and missing are excluded before the probe, so they are covered as written.

Nits

  • hevcPreviewLint.ts:238-290 — every local video is now probed twice per check (HEVC codec, then duration). One -show_entries stream=codec_name,duration:format=duration call could feed both rules.
  • hevcPreviewLint.ts:184-190 — slots authored with data-end but no data-duration are skipped. Consistent with data-duration being canonical, just noting the warning is best-effort there.

Verdict: APPROVE
Reasoning: Render now matches the runtime's hold for finite non-looping slots past EOF through the existing final-frame path, loop and open-ended slots still error exactly as before, and every planner change fails a named test when reverted; the two gaps are fixtures that do not discriminate, not behaviour.

— Miga

@miga-heygen
miga-heygen merged commit f3bd423 into main Sep 15, 2026
56 checks passed
@miga-heygen
miga-heygen deleted the bugfix/1787766784-media-start-eof branch September 15, 2026 19:10
valeriangalliat added a commit to ArcadeHQ/hyperframes-next that referenced this pull request Sep 15, 2026
valeriangalliat added a commit to ArcadeHQ/hyperframes-next that referenced this pull request Sep 15, 2026
valeriangalliat added a commit to ArcadeHQ/hyperframes-next that referenced this pull request Sep 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

render refuses a <video> whose data-media-start is past the end of the file (media_start_out_of_range) while the runtime holds its last frame

2 participants