Skip to content

crossfade_concat: frame-exact chain; fix silent xfade truncation - #17

Merged
TroyHernandez merged 6 commits into
mainfrom
fix/frame-exact-xfade-chain
Jul 15, 2026
Merged

crossfade_concat: frame-exact chain; fix silent xfade truncation#17
TroyHernandez merged 6 commits into
mainfrom
fix/frame-exact-xfade-chain

Conversation

@TroyHernandez

Copy link
Copy Markdown
Contributor

ffmpeg's xfade silently truncates when offset + duration passes the first input's end by any amount — even float dust — and %g-formatted seconds guarantee dust. Seconds-based trim windows additionally lose ±1 frame to rounding, and the error compounds across a chain: an 8-chunk track assembled 8.3s of motion out of 30.8s, then tpad froze the last frame (burned caption included) for 22 seconds.

Repro (silent, no warnings): [a][b]xfade=duration=0.0416667:offset=3.66667 where input a is 3.7083333s → 103 frames out instead of 189. offset=3.65625 (quarter frame early) → 189. The 9-frame fades used previously stayed under the boundary by rounding luck; the container backend's 1-2 frame conditioning heads surfaced it.

  • trim=start_frame/end_frame for frame-exact feeds (seconds trims dropped a frame: 97 vs 98)
  • nb_frames-based feed durations, integer frame accumulator
  • quarter-frame early bias on every xfade offset; %.6f formatting
  • API unchanged (fade/windows still seconds); callers: render_timeline, cornductor::render_chained

Verified: the failing 8-chunk timeline now assembles 741 frames / 30.875s with motion throughout.

DESCRIPTION: single-quote 'FFmpeg' / 'OpenTimelineIO' / 'rotio' in Title
and Description, add reference URLs for FFmpeg and OpenTimelineIO, and add
URL/BugReports pointing at the GitHub repo. Add an @examples block to the
exported align_overlaps(), and @param/@return to the internal rms helpers.
Examples stay \dontrun: they need the ffmpeg binary (a SystemRequirement,
not guaranteed on CRAN check hosts) plus user-supplied media files.
ffmpeg's xfade truncates its output silently when offset + duration
lands past the first input's end by even float dust, and seconds-based
trim windows lose a frame to rounding; across several joins the drift
compounds and the chain collapses (a 30s track rendered 8s of motion,
then tpad froze the last frame -- caption and all -- for 22s).

Run the whole chain in frame arithmetic: trim=start_frame/end_frame for
frame-exact feeds, nb_frames-based durations, an integer frame
accumulator, and a quarter-frame early bias on every xfade offset so
the dissolve always ends strictly on the first input's last frame.
@TroyHernandez
TroyHernandez merged commit 9b92d4c into main Jul 15, 2026
@TroyHernandez
TroyHernandez deleted the fix/frame-exact-xfade-chain branch July 15, 2026 05:56
TroyHernandez added a commit that referenced this pull request Jul 20, 2026
* Add still_clip(), frames_clip(), audio_concat()

Three call-by-call primitives for slide/scene assembly:

- still_clip(): one image -> a video clip, static or animated by a
  deterministic Ken Burns pan/zoom (zoompan with eased zoom + anchor
  expressions, crop clamped to frame). Same encode settings as
  crossfade_concat() so outputs stay concat-compatible.
- frames_clip(): a numbered frame sequence -> a video clip; the landing
  pad for programmatically drawn animations.
- audio_concat(): join audio files through the concat filter with exact
  anullsrc silences between and after them, normalizing rate/layout;
  built for assembling a narration bed from per-segment TTS takes.

* rformat + document

* crossfade_concat: probe fps lazily for the single-clip passthrough

Since the frame-exact chain (#17), crossfade_concat probed videos[1]
unconditionally, so the single-clip dry_run path errored on files that
don't exist yet (test_crossfade.R line 6 has been failing since). Only
probe when a join, window, or audio overlay actually needs the rate.

* Bump version to 0.1.0.10
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