Skip to content

fix(export): put the whole timeline on one frame grid - #1163

Merged
EtienneLescot merged 1 commit into
mainfrom
fix/export-frame-grid
Oct 13, 2026
Merged

EtienneLescot merged 1 commit into
mainfrom
fix/export-frame-grid

Conversation

@EtienneLescot

@EtienneLescot EtienneLescot commented Oct 12, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

An export now lasts its timeline's programme length, to one frame, however many trims, regions and clips it has. Each speed span used to round its own frame count up, which added up to a frame per span.

Fixes #1162

Candidate for cherry-pick to release/v2.1.0.

Change

  • One frame grid (speed_segments_for_window): frame k sits at programme time k / fps. Programme time carries across spans, trims and clip junctions, and each span emits the grid frames inside it. The total is ⌈(P − 1 ms) × fps⌉ for P programme seconds.
  • Source sampling (SpeedSegment::source_time): each span samples the source where the grid puts its frames, instead of restarting its cadence at the span start.
  • Export walk: walk_composited_timeline carries programme time from clip to clip. A clip cut short by its decoder resyncs the grid to the real frame counter.
  • Preview (ProgrammeClock): it chains the clips the same way, so preview and export still agree at every frame.
  • Progress total (outputFrameCount.ts): it mirrors the grid, so the UI total equals the encoder's count.
  • Audio: unchanged. It is stretched to each span's frame count, so it follows the grid.
  • Docs: the checklist's export section now tells testers how to predict the count exactly, and why a correct export can look one frame off. export-pipeline.md describes the grid.

Verification

CLI export, 20 s synthetic source, 60 fps. Counts are video packets.

Project Programme frames Before (installed RC) After
one trim 1079.4 1080 1080
one 1.5x region 1119.8 1121 1120
12 trims 976.8 984 977
QA project (2 clips, 14 spans) 4886.8 n/a 4887
  • Progress total: equal to the exported count on every synthetic project.
  • Audio: lasts frames ÷ fps on every project.
  • Tests: cargo test -p openscreen-compositor --lib passes (413 tests), and so does vitest src/lib/exporter. New cases cover the QA project (2445 frames, was 2452) and 200 windows (1750 frames, was 2000), on both the Rust and the TS side.
  • Typecheck and lint: both tsc configs and Biome are clean.

Not covered

  • Progress total on the QA project: still 3 frames high, because it uses the declared clip length (48.789 s, the audio track's) where the walk clamps to the video's 48.765 s. This is the known limitation in outputFrameCount.ts.
  • Real-time preview: not exercised. Only its clock is unit-tested.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes
    • Improved export frame timing across clips and speed changes, reducing cumulative rounding discrepancies and helping exported video durations match expected frame counts.
    • Corrected the expected frame count for an empty-region export.
  • Documentation
    • Clarified how export frame counts and durations are calculated, including common reasons reported video and audio durations may differ.

Each speed span rounded its own frame count up, so an export ran up to
one frame per span longer than its timeline (+7 frames on the 14-span
v2.1.0 QA project). Spans now take their frames from a single programme
grid carried across trims and clips, and sample the source where that
grid puts them. The progress total mirrors it.

Part of #1162.
@coderabbitai

coderabbitai Bot commented Oct 12, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 28763e80-ab83-4a8a-9d93-83ec87eca75c

📥 Commits

Reviewing files that changed from the base of the PR and between 9c55596 and 8bc09ee.


📒 Files selected for processing (6)
  • crates/compositor/src/regions.rs
  • crates/compositor/src/timeline_walk.rs
  • src/lib/exporter/outputFrameCount.test.ts
  • src/lib/exporter/outputFrameCount.ts
  • technical-documentation/architecture/export-pipeline.md
  • technical-documentation/testing/manual-e2e-checklist.md

Included review availability: This review used your included allowance. Your plan provides up to 8 included reviews per hour; 3 remain after this review.



📝 Walkthrough

Walkthrough

Export frame placement and frame-count estimates now use cumulative programme time across spans and clips. The compositor samples each segment on the shared frame grid. Regression tests and export documentation cover cumulative totals and duration interpretation.

Changes

Cumulative programme-time frame grid

Layer / File(s) Summary
Segment grid and programme clock
crates/compositor/src/regions.rs
SpeedSegment records its programme-time start and first frame. Segment frame counts use the shared grid, and ProgrammeClock maps source positions to programme frames. Tests cover totals across clips and spans.
Timeline export sampling
crates/compositor/src/timeline_walk.rs
The export walk carries programme time between clips and uses SpeedSegment::source_time to select source timestamps. It updates programme time based on planned or produced frames.
Frame-count parity and documentation
src/lib/exporter/outputFrameCount.ts, src/lib/exporter/outputFrameCount.test.ts, technical-documentation/architecture/export-pipeline.md, technical-documentation/testing/manual-e2e-checklist.md
Frame-count estimation accumulates programme-time endpoints across clips. Tests cover updated and multi-span totals. Documentation describes the shared frame grid, total-frame rule, and duration interpretation notes.

Priority: ➖ Normal

Estimated code review effort: 3 (Moderate) | ~20 minutes

Change: Bug fix · Severity of issue fixed: Medium

Sequence Diagram(s)

sequenceDiagram
  participant walk_composited_timeline
  participant speed_segments_for_window
  participant SpeedSegment
  walk_composited_timeline->>speed_segments_for_window: Build segments with cumulative programme time
  speed_segments_for_window-->>walk_composited_timeline: Return segments on the shared frame grid
  loop Each output frame
    walk_composited_timeline->>SpeedSegment: source_time(i, fps)
    SpeedSegment-->>walk_composited_timeline: Return source timestamp
  end
  walk_composited_timeline->>walk_composited_timeline: Advance programme time after the clip
Loading

Merge Risk: ⚪ Minimal · up to 8bc09

No blocking issue was identified; the change is mergeable after normal checks.

Pre-merge checks | Passed 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check Passed The title clearly and concisely describes the main change: using one frame grid across the entire export timeline.
Description check Passed The description is mostly complete. It explains the change, links issue #1162, identifies the implementation areas, documents testing results, and states known limitations. It does not include the tem…
Linked Issues check Passed Issue #1162 is directly linked and has coding requirements. SpeedSegment now stores cumulative programme time and the first grid frame. source_time samples from that shared grid. `walk_composited_…
Out of Scope Changes check Passed The changes stay within issue #1162. The modified compositor, timeline walk, preview clock, progress-count code, tests, and export documentation all implement or verify the shared frame-grid behavior.…
Docstring Coverage Passed Docstring coverage is 85.71% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 21 functions across 4 files. (2 skipped: 2 …

✨ Finishing Touches
📝 Generate docstrings
  • Commit to this branch
  • Create a new PR

🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR

  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Warning

Some tools did not complete. Review the errors below.

🔧 Clippy (1.98.1)

Clippy execution failed



Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@EtienneLescot

Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Oct 12, 2026 •

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@EtienneLescot
EtienneLescot merged commit 2c978f3 into main Oct 13, 2026
20 checks passed
@EtienneLescot
EtienneLescot deleted the fix/export-frame-grid branch October 13, 2026 07:49
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.

Export runs up to one frame per span longer than its timeline

1 participant