Skip to content

fix(recording): preserve frame timing#33

Merged
kitlangton merged 1 commit into
mainfrom
recording-cleanup
Jul 19, 2026
Merged

fix(recording): preserve frame timing#33
kitlangton merged 1 commit into
mainfrom
recording-cleanup

Conversation

@kitlangton

Copy link
Copy Markdown
Contributor

What

Preserve ImageFrame.atMs timing when exporting constant-rate MP4 recordings, while reducing capture and filesystem work for dense or unchanged terminal output.

Before / After

Before: FFmpeg received -r as an input option over one concat entry per source frame. Irregular frames at 0 ms, 1000 ms, and 1200 ms encoded as three 5 FPS frames lasting 0.6 seconds, discarding the supplied timing. Replay also captured the complete terminal grid after every event, and equal snapshots with different object identities rendered separately.

After: Drive samples source timestamps onto the requested CFR grid before invoking FFmpeg. The same input becomes seven 5 FPS frames lasting 1.4 seconds, with the final frame preserved. Replay captures lazily at sample boundaries, and structurally equal snapshots share one render key.

How

  • src/recording/encode.ts selects frames from atMs, references unique PNGs directly from the concat manifest, and removes per-sample hardlinks.
  • src/recording/replay.ts captures terminal state only when a requested sample needs it.
  • src/recording/export.ts caches a structural digest once per snapshot object so equal states deduplicate across identities.
  • src/recording/frame-rate.ts centralizes the 60 FPS default and positive-finite validation.
  • Recording regressions cover irregular timestamps, a genuinely off-grid final state, dense event streams, invalid rates, and equal-snapshot deduplication.

Scope

This keeps 60 FPS as the default and retains the existing custom fps quality/performance control. It does not change timeline capture, terminal rendering, or the public result shape.

Testing

  • bun run test:effect test/recording/export.test.ts test/recording/replay.test.ts test/recording/export-dedupe.test.ts (23 passed)
  • bun run test:effect (147 passed)
  • bun run test:cli (51 passed)
  • bun run lint
  • bun run typecheck
  • bun pm pack --dry-run
  • FFprobe verifies exact 5 FPS and 60 FPS output, duration, frame count, and final-frame pixels.

Flow

flowchart LR
  Timeline[Timeline events] --> Replay[Lazy replay sampling]
  Replay --> Frames[Timestamped frames]
  Frames --> CFR[CFR frame selection]
  CFR --> Unique[Unique rendered PNGs]
  Unique --> MP4[FFmpeg MP4]
Loading

@kitlangton
kitlangton merged commit e66adc1 into main Jul 19, 2026
1 check passed
@kitlangton
kitlangton deleted the recording-cleanup branch July 19, 2026 16:06
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.

1 participant