Skip to content

AE#418 round 2: an axis offset composes, it is not owned by a decode run - #429

Merged
superuser404notfound merged 1 commit into
mainfrom
fix/ae418-axis-composes
Aug 25, 2026
Merged

AE#418 round 2: an axis offset composes, it is not owned by a decode run#429
superuser404notfound merged 1 commit into
mainfrom
fix/ae418-axis-composes

Conversation

@superuser404notfound

Copy link
Copy Markdown
Owner

Follow-up to #420. rrgomes retested 6.43.0 and relation (3) failed, in a case the fix did not anticipate: the decode-run re-anchor fired for a seek that had not left the re-aimed run, so the axis went to 0 while AVPlayer was still playing content 14 s below it, and the captions ran that far ahead of the picture again.

Reproduced on the lying-Cues fixture with play --picture-probe, deterministically: a resume at 53 s (gate re-aims and opens 9 s below its boundary) followed by a seek to 80 s reads capErr=-8.983, with axisErr unchanged at -9.000 throughout. The picture never moved; only the published axis did.

What the measurement says

Round 1 keyed the offset to "the decode run AVPlayer began here" and answered that from the fetch order. Every fetch out of sequence was called a fresh run. It is not one: AVPlayer asks for a segment below its target on a seek, and it asks out of order while continuing the run it is already playing.

What the axis actually turns on is placement, and the offset composes. AVPlayer places a segment at its advertised start read through the mapping its timeline already carries, so re-placing an overlong segment adds its offset again. Measured at re-aims of 0.5, 0.875, 1, 3, 5, 7, 9 and 11 s:

case true axis round 1 published
resume 53 (gate 9 s below its boundary) -9.000 -9.000
... then a seek onto an axis-true segment -9.000 0.000
... then a seek that re-places that same segment -18.000 0.000
... then a seek whose restart re-aims 5 s more -14.000 -5.000
tiered fixture, -11 re-placed -22.000 0.000

One exception, also measured: AVPlayer discards a sub-second axis at a seek and snaps back to the playlist. -0.500 and -0.875 read axisErr=0.000 after one; -1.000, -1.083, -1.292, -1.500, -3, -4, -7, -9 and -11 all survive unchanged.

The change

  • The axis moves when AVPlayer places a segment, by exactly what that segment carries below its advertised start, and the seam sits at that advertised start read through the axis in effect before it landed.
  • The per-index record replaces the single anchor pair: several epochs can leave an overlong segment in the cache at once. A new epoch drops the entries at and above its own index, which are rewritten axis-true.
  • The gate no longer publishes on its own. It records what the segment is worth, and the placement publishes it, so an epoch AVPlayer never fetches from cannot move the clock.
  • The VOD seek path publishes the sub-second snap from the landing forward.

Verification

Thirteen arms of the fixture matrix, including the four that read -8.983 before, now read capErr=+0.017, one frame at 24 fps. The control fixture (Cues that ARE its sync samples) is untouched on both arms. swift test: 2100 tests in 296 suites, 0 failures.

🤖 Generated with Claude Code

https://claude.ai/code/session_01RbWZLwBVLGM1xUVXa9NeJ1

…418)

Round 1 fixed what the offset IS and got its lifetime wrong. It keyed the
offset to "the decode run AVPlayer began here" and answered that from the
fetch order: any request that did not follow its predecessor. rrgomes'
seek burst falsified it. A fetch out of sequence happens while AVPlayer
stays inside the run it is already playing, and the axis was then dropped
to zero under a picture that had not moved, which is the pre-fix shape
re-entered through the burst.

Measured with `play --picture-probe` on the lying-Cues fixture, at re-aims
of 0.5, 0.875, 1, 3, 5, 7, 9 and 11 s:

  AVPlayer places a segment at its advertised start read through the
  mapping its timeline ALREADY carries, so the offset COMPOSES.

A resume that opened 9 s below its boundary reads axisErr -9.000. A seek
that makes AVPlayer fetch that same segment a second time reads -18.000,
one that provokes a restart re-aiming 5 s more reads -14.000, and the
reporter's shape (a seek onto an axis-true segment) reads -9.000 still.
Round 1 published 0.000 for all three, so capErr read -8.983 where the
picture had not moved at all.

So the axis moves when AVPlayer PLACES a segment, by exactly what that
segment carries below its advertised start, and the seam belongs at that
advertised start read through the axis in effect before it landed. Only an
epoch's first segment can carry anything; every later one is cut on its own
boundary. The record is keyed by index now, because several epochs can
leave such a segment in the cache at once, and a new epoch drops the
entries at and above its own index (those are rewritten axis-true).

One exception, also measured: AVPlayer discards a sub-second axis at a seek
and snaps back to the playlist. -0.500 and -0.875 read axisErr 0.000 after
one; -1.000, -1.083, -1.292, -1.500, -3, -4, -7, -9 and -11 all survive
unchanged. The VOD seek path publishes that snap from the landing forward.

Thirteen arms of the fixture matrix, including the four that read -8.983
before, now read capErr +0.017, which is one frame at 24 fps.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RbWZLwBVLGM1xUVXa9NeJ1
@superuser404notfound
superuser404notfound merged commit 165285a into main Aug 25, 2026
4 checks passed
@superuser404notfound
superuser404notfound deleted the fix/ae418-axis-composes branch August 25, 2026 21:42
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