Fix the 72.7s A/V gap: xfade offsets ran off the end of their input

_xfade_chain positioned every transition using _audio_dur, which probes
format=duration, which is max(video, audio). A clip's audio outlasts its
video by about a frame, so the offset accumulator crept ahead of the real
picture timeline. Once the creep exceeded the transition width, xfade
emitted the transition and silently discarded the second input and every
clip downstream, exiting 0 with nothing on stderr. That is the whole of
the shipped chapter's 436.39s of video over 363.67s of audio.

Offsets now come from min(video, audio). Every input is floored to a
whole frame count and trimmed on both streams, so the accumulator tracks
the real timeline instead of estimating it. _check_assembled verifies
each encode against the predicted length and against its own audio,
because both assembly branches drop stream time without failing.

Verified over the 49 real clips of chapter 7c944dd4: the round that
turned 359s of video into 100s now loses 0.85s, and the chapter comes out
358.76s video against 358.76s audio.

The single-item passthrough was not the cause. Two round-0 groups of 8
fresh clips collapse without one, recorded void in decisions/.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-12 11:52:38 +04:00
parent a9d64fe80a
commit 1457556ce3
6 changed files with 206 additions and 20 deletions
+3
View File
@@ -47,6 +47,9 @@ Machine split: workers run on **workpc** (RX 7900 GRE, ROCm). MinIO and the orch
shape means reconciling the orchestrator in the same session. Neither repo's self-checks can catch a
contract break, because each asserts its own side.
8. **`ponytail:` comments mark deliberate simplifications** and name the upgrade path. Respect them.
9. **Nothing that positions an ffmpeg filter may use `format=duration`.** It reports `max(video, audio)`.
It hides A/V drift, and it walks xfade offsets past the end of their input. ffmpeg then discards clips
and still exits 0 (`decisions/chapter-assembly.md#offsets-from-min-stream`).
## Working rules