Commit Graph
310 Commits
Author SHA1 Message Date
James bcbe7f1104 fix(producer): fallback distributed capture safely 2026-07-27 03:08:40 +00:00
James RussoandGitHub a24b078df9 fix(producer): accept partial color metadata in plan v2 (#2814)
* fix(producer): accept partial color metadata in plan v2

* test(engine): make partial color probe hermetic

* fix(producer): validate plan v2 sentinels in fallback mode
2026-07-26 21:48:51 -04:00
James 05549e631b fix(gcp): enforce effective BeginFrame capture 2026-07-27 00:02:37 +00:00
James 66e44e77dc fix(render): aggregate extraction launch failures 2026-07-26 19:47:33 +00:00
James a142085ca2 fix(producer): narrow extraction error shapes honestly 2026-07-26 19:47:33 +00:00
James 18ff1f885f fix(producer): type video extraction failures 2026-07-26 19:47:33 +00:00
James 3d93eb037a fix(engine): block future-use IPv4 downloads 2026-07-26 19:00:41 +00:00
James c8b749b10d fix(engine): close downloader trust-boundary gaps 2026-07-26 19:00:41 +00:00
James bf89116243 fix(engine): narrow network error shapes honestly 2026-07-26 19:00:41 +00:00
James 15696acf90 fix(engine): make video downloads atomic and retry transient failures 2026-07-26 19:00:41 +00:00
Miguel Ángel ea1b4d6e77 fix(producer): normalize padded audio on sample timeline 2026-07-25 21:14:18 +00:00
Miguel Ángel 1a791b17f1 fix(engine): stop mux at shortest normalized stream 2026-07-25 21:14:18 +00:00
Miguel Ángel 6a5608777d test(render): cover duration-capped audio mux 2026-07-25 21:14:18 +00:00
Miguel Ángel ed0755e5b2 fix(render): cap final mux to video duration 2026-07-25 21:14:18 +00:00
Miguel Ángel d6a2efd745 fix(render): scope M4A priming preservation to trims 2026-07-25 21:14:17 +00:00
Miguel Ángel 45f59fe5c8 fix(render): preserve normalized M4A edit timing 2026-07-25 21:14:17 +00:00
James RussoandGitHub 4214f36cb3 fix(engine): preserve bounded ffprobe diagnostics (#2772)
* fix(engine): preserve bounded ffprobe diagnostics

* test(engine): harden ffprobe diagnostic redaction
2026-07-25 17:12:43 -04:00
James RussoandGitHub 02fe77b2f9 fix(audio): preserve causes and use portable padding (#2769)
* fix(audio): preserve causes and use portable padding

* fix(audio): address failure taxonomy review
2026-07-25 17:12:31 -04:00
ukimsanov 0d27b5fbe6 feat(runtime): render media treatments deterministically 2026-07-24 11:17:22 -07:00
Vance Ingalls 69f7439992 fix(engine): lazy-promisify execFile in psnr so partial child_process mocks don't crash at import 2026-07-23 17:57:15 -07:00
Vance Ingalls 1fd80dbad4 fix(producer): verify sequential disk drawElement samples too 2026-07-23 15:07:35 -07:00
Vance Ingalls 1117a7a08d fix(engine): self-verify parallel disk drawElement samples (PRINFRA-352) 2026-07-23 14:41:25 -07:00
Vance IngallsandGitHub e3f03a17b0 Merge pull request #2723 from heygen-com/07-21-fix_engine_worker_autoscaler_memory_budget
fix(engine): realistic worker memory budget + sizing/feedback telemetry
2026-07-21 21:44:35 -07:00
Vance Ingalls a8085e6a6d fix(producer): emit heap advisory at orchestrator, lock message + telemetry props with tests 2026-07-21 20:39:06 -07:00
Xuanru LiandGitHub 429c15387e Revert "feat(producer): renderStretch to re-time short compositions across longer scenes (#2676)" (#2730)
This reverts commit a2166bc726.
2026-07-21 19:39:03 -07:00
Vance Ingalls 74dd4eafee fix(engine): realistic worker memory budget + sizing/feedback telemetry 2026-07-21 14:31:14 -07:00
Vance IngallsandGitHub ef51f73516 Merge pull request #2681 from heygen-com/07-21-feat_engine_warn_on_live_map_viewports_at_capture_init
feat(engine): warn when a live map viewport is detected at capture init
2026-07-21 14:07:43 -07:00
Xuanru LiandGitHub a2166bc726 feat(producer): renderStretch to re-time short compositions across longer scenes (#2676)
Linear: VA-1859

## Problem

For a `fit_to_scene` B-roll where the composition's intrinsic timeline (e.g. `data-duration=1.0s` → 30 frames) is shorter than the scene it fills (e.g. 4.8s narration), the producer renders only the intrinsic 30 frames and the downstream compositor frame-holds/PTS-stretches that fixed clip to the scene length. Spreading 30 unique frames over 4.8s starves motion to ~6 effective fps → a visibly choppy result. Root cause: the producer welds one `composition.duration` to both the frame count and the 1:1 seek mapping, with no notion of a target output length.

## Fix

Add optional `renderStretch: number` (default `1.0` = no-op), `renderStretch = intrinsic / target`:

- **Frame count** comes from the target: `outputDuration = intrinsic / renderStretch`, `totalFrames = outputDuration × fps` (`probeStage.ts`). `composition.duration` stays intrinsic (drives video/audio windows).
- **Per-frame seek** is scaled: `time = (frameIndex / fps) × renderStretch`, so the N output frames map across `[0, intrinsic]` — a fresh frame per output frame.

All seek sites go through a single shared `outputFrameToTimelineSeconds(frameIndex, fps, renderStretch)` helper (`core.types.ts`), consumed by every capture path so none can silently diverge:
- parallel (`parallelCoordinator.ts`), `sdr_streaming` (`captureStreamingStage.ts` ×3), `sdr_disk` (`captureStage.ts`), HDR loops.
- DrawElement + static self-verify (`frameCapture.ts`) — ground-truth seek uses the same mapping, so PSNR compares like-for-like (no spurious verification failure on stretched comps).
- Distributed path: `renderStretch` threaded through `DistributedRenderConfig` → chunk workers, and **folded into the plan hash only when `!= 1`** so a pre-stretch cached plan is never reused.

With `renderStretch = 1` (or omitted → `?? 1`): every seek is `×1.0` (IEEE-754 identity), frame counts unchanged, and the plan hash is byte-identical — a provable no-op. `player.ts` absolute-seek is untouched.

## Verify

- typecheck (core + engine + producer): pass. lint/format/fallow/commitlint: pass. `planHash` + `renderRequest` unit suites: pass.
- Adversarial self-review found + fixed three capture-path gaps (streaming, self-verify, distributed) before this revision.
- **Not yet runtime-verified** on a real render — needs a fit_to_scene render at `renderStretch < 1` confirming N distinct frames over the target length (draft until then).

Paired with experiment-framework#42766, which computes and forwards `renderStretch = hf intrinsic / scene duration`.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-07-21 13:30:44 -07:00
Vance Ingalls f86a14f9db feat(engine): warn when a live map viewport is detected at capture init 2026-07-21 01:08:02 -07:00
Vance Ingalls 120d5f1a38 fix(engine): carry frameStride onto WorkerResult (fixes interleaved worker false-positive) 2026-07-21 00:26:24 -07:00
James 977e4e7911 fix: bound invalid render durations 2026-07-21 03:05:53 +00:00
James c5186729a1 test(engine): isolate peer abort regression 2026-07-20 11:02:05 -07:00
James 003087e924 fix(producer): preserve render request config contracts 2026-07-17 17:22:34 -04:00
James 8d4c53ba22 fix(producer): validate render request engine snapshots 2026-07-17 16:22:23 -04:00
Miguel ÁngelandGitHub 52e518d356 fix(engine): stop compositing phantom duplicates on captureBeyondViewport (#2607)
* fix(core): stop the async media-metadata rebind once render capture starts seeking

scheduleMetadataDurationHydration re-resolves and can swap the captured
GSAP timeline off a debounced loadedmetadata/durationchange event, fully
uncoordinated with the producer's own per-frame renderSeek calls. When a
full-length <video>'s metadata resolves after capture has already begun
(slow I/O, Docker), this races the deterministic BeginFrame capture loop
and can reflow sub-composition state mid-render, producing phase-offset
duplicate content in captured frames (#2550).

Render-mode duration correction already happens deterministically during
the probe stage before capture starts, so once renderSeek has been called
once there is nothing left for this self-correction to do — gate it off
for the rest of the session.

* fix(core): scope the metadata-rebind guard to actual render/export pages

renderSeek isn't capture-exclusive — Studio's own preview iframe falls
back to it for compositions whose timeline overhangs every native
adapter's duration. Gating the HF#2550 fix on renderCaptureSeekStarted
alone silently disabled the metadata-driven duration self-correction for
that live-scrub case too, where it's still needed. Require the render/
export page signal (window.__HF_EXPORT_RENDER_SEEK_CONFIG, set only by
the producer's fileServer.ts) alongside it, and add a regression test
covering the Studio-preview case.

* fix(engine): stop requesting beyond-viewport capture for video comps that don't need it

Root-caused HF#2550 by reproducing the reporter's public repro end-to-end
(not just the timeline-rebind mechanism from the earlier commits in this
branch) on native Linux: instrumented the actual DOM state during a real
capture session and confirmed the sub-composition never double-mounts —
getBoundingClientRect and the timeline's own local time both match the
single, correct DOM tree throughout. The phantom second copy only exists
in the captured screenshot pixels.

Bisected it to captureBeyondViewport: resolveVideoCaptureBeyondViewport
(#1094's tall-portrait fix) forces `Page.captureScreenshot`'s beyond-viewport
path on for any render with a native <video>, regardless of whether the
page's content actually overflows the declared capture height. On
SwiftShader that beyond-viewport path can composite a stale, vertically
offset paint of the page alongside the fresh one for content that fits
entirely within the viewport — producing exactly the reported phase-offset
duplicate. Disabling captureBeyondViewport (repro's video still present)
eliminates the duplicate outright; re-enabling it reproduces the duplicate
byte-for-byte, isolating it as the actual cause.

Adds pageContentExceedsCaptureHeight, a ground-truth measurement of the
page's actual scrollHeight against the requested capture height, and wires
it into initializeSession to downgrade captureBeyondViewport back to false
once the page is settled and it's confirmed unnecessary — the "reliable
clip predictor" the original #1094 fix's ponytail comment flagged as
missing. This keeps #1094's fix intact for content that genuinely
overflows while closing the SwiftShader ghosting hazard for the (common)
case of video that fits inside its own viewport.

* test(producer): add HF#2550 video+sub-composition regression fixture

Checks in the reporter's confirmed real-world reproduction (media
regenerated via ffmpeg testsrc2, matching their public repro repo) as a
regression fixture, with a golden baseline rendered against the fix.

Verified end-to-end via the project's own Docker regression harness:
- Rendering this fixture with the fix produces the golden baseline
  (clean, single flowchart instance, captureBeyondViewport correctly
  downgraded).
- Direct CLI renders (not through this harness) against unpatched code
  reproduce the reported phantom-duplicate artifact reliably (10/10).

Caveat documented in meta.json: the underlying bug is timing-dependent.
Two harness runs against unpatched code, using this same fixture, did
not reproduce the artifact (0/2) — the harness's in-process render path
apparently doesn't hit the same race window a direct CLI process does on
this host. This fixture is a best-effort regression guard and a
preserved real-world repro, not the sole protection — the deterministic
guard is packages/engine/src/services/screenshotService.test.ts's
pageContentExceedsCaptureHeight unit tests, which exercise the actual
fix logic directly.

Also adds an .gitattributes LFS rule for this fixture's source
index.html (744 KB — carries the real project's embedded base64
assets, over the largefiles hook's 500 KB non-LFS limit).

* fix: route HF#2550 fixture binaries through LFS (were committed raw)

filter.lfs.clean/smudge were locally configured as a no-op "cat" in
this repo's shared .git/config, silently disabling LFS filtering for
every worktree. The previous commit's large binaries (output.mp4,
compiled.html, source index.html, source video) landed as raw blobs
instead of LFS pointers as a result. Ran `git lfs install --local
--force` to restore the correct filter commands, then re-staged the
affected files so they commit as proper LFS pointers.

* fix(engine): address capture viewport review feedback
2026-07-17 11:59:45 -04:00
James b4d85abe3e fix(engine): avoid polynomial capture failure regex 2026-07-17 05:22:53 -04:00
James d11dad6f6d refactor(engine): type capture failures 2026-07-17 04:10:30 -04:00
Vance IngallsandGitHub 972ec7af03 Merge pull request #2564 from heygen-com/via/win-workdir-env
feat(cli): surface extract-cache dir in doctor + add --frames-cache-dir sugar
2026-07-17 00:43:06 -07:00
James RussoandGitHub a5f4063d9d refactor(engine): manage child process lifecycles (#2160)
* refactor(engine): manage child process lifecycles

* fix(engine): preserve child reaping after runtime errors

* fix(engine): untrack child processes on exit
2026-07-17 01:17:53 -04:00
Miguel Angel Simon Sierra 2542977e2f refactor(parsers): single shared FFmpeg/FFprobe binary resolver
The cli, engine, and lint packages each carried their own copy of the
ffmpeg/ffprobe lookup, annotated fallow-ignore code-duplication, and the
copies had drifted: the engine copy handled Windows PATHEXT and executed
which/where without a shell but lacked the Homebrew-dirs fallback for
GUI-spawned processes; the cli copy had the opposite. One resolver in
@hyperframes/parsers (the dependency-graph bottom) now carries the union
of both hardenings, and all three packages delegate to it. Every
consumer gets strictly more robust resolution; env-override semantics
per call site are preserved via configuredMustExist.
2026-07-16 18:36:40 -04:00
Miguel ÁngelandGitHub dc05fa57dc fix(engine): ignore benign media request aborts (#2423) 2026-07-16 18:20:25 -04:00
Miguel ÁngelandGitHub c1ebb9e52a fix(engine): reuse HyperFrames browser cache (#2459)
* fix(engine): reuse HyperFrames browser cache

* test(engine): isolate browser cache home on Windows
2026-07-16 18:20:21 -04:00
ViaandVia <via-heygen[bot]@users.noreply.github.com> 81c15ded2f feat(cli): surface extract-cache dir in doctor + add --frames-cache-dir sugar
Windows users with the OS temp dir on a small system drive have hit
C: exhaustion mid-render (Slack ts=1784219488 · CLI v0.7.58 · win32
15 GB / 8-core, ~5500 frames). The engine already honors
HYPERFRAMES_EXTRACT_CACHE_DIR for relocation, but the knob was
undocumented and invisible in diagnostics — the reporter had to piece
together a 4-flag compound workaround including EXTRACT_CACHE_DIR=off.

Changes:
- Extract the env-var resolver into a public engine API
  (resolveExtractCacheDir, defaultExtractCacheDir,
  EXTRACT_CACHE_DIR_DISABLED_ALIASES) with a typed resolution shape
  distinguishing "disabled by user" vs "default" vs "env override".
- Add a Frames-cache check to `hyperframes doctor` that reports the
  effective directory, its free space, source (env or default), and
  fails with a relocation hint when <2 GB free at that mount.
- Add `hyperframes render --frames-cache-dir <path>` as discoverable
  CLI sugar for the env var, including the opt-out aliases
  (off/none/false/0) and CWD-safe absolute-path resolution.
- Document the flag in docs/packages/cli.mdx with the field-signal
  citation, and add a render example row for the Windows workflow.
- Cover both surfaces with unit tests (6 doctor cases + 4 engine
  cases including all disabled-alias variants).

Refs Slack #hyperframes-cli-feedback ts=1784219488 (win32 v0.7.58).

Co-authored-by: Via <via-heygen[bot]@users.noreply.github.com>
2026-07-16 18:24:43 +00:00
James 751b2566de fix(engine): escalate hung browser lease closes 2026-07-16 12:58:10 -04:00
James fd398a0ef9 refactor(engine): add fingerprinted browser leases 2026-07-16 12:58:10 -04:00
Miguel ÁngelandGitHub 262845410e fix(engine): preserve mono audio level (#2392) 2026-07-16 12:03:17 -04:00
Miguel ÁngelandGitHub 6a935bfc02 Merge pull request #2521 from heygen-com/fix/stale-compositor-layer
fix(renderer): prevent stale SwiftShader layers
2026-07-16 01:23:59 -04:00
Miguel Ángel 5d33ac63a0 docs(renderer): link SwiftShader workaround tracker 2026-07-16 05:19:00 +00:00
Miguel Ángel d5c2a29cc8 fix(renderer): prevent stale SwiftShader layers 2026-07-16 04:56:36 +00:00
James RussoandGitHub 4c7b010bac fix(engine): emit SystemMemory cgroup notice to stderr, not stdout (#2520) 2026-07-16 00:36:25 -04:00