Skip to content

A re-aimed gate opens on the first sync sample above its backoff, not the nearest one below the boundary, so it lands further back than the source requires #423

Description

@superuser404notfound

Found while measuring #418, and only visible once the clock stopped hiding it.

What happens

When a plan boundary turns out not to be a random-access point, reanchorGateBelowBoundary steps back 4, then 8, then 16, then 32 s and seeks there. Demuxer.seek sets min_ts == ts, so each attempt opens on the first sync sample AT OR ABOVE where it aimed. What the segment actually needs is the LAST sync sample at or below the boundary, and those are not the same sample whenever the backoff overshoots.

Measured on tc-cues-lie.mkv (Scripts/timecode-fixture.sh plus Scripts/mkv-cue-fixture.py, sync samples at 38.417 and 43.0 with a drought to 55.0), resuming at 53 s, boundary 52.000:

boundary 52000 is not a random-access point; re-aimed the gate 4s below it at 48000  (attempt 1/4)
boundary 52000 is not a random-access point; re-aimed the gate 8s below it at 44000  (attempt 2/4)
boundary 52000 is not a random-access point; re-aimed the gate 16s below it at 36000 (attempt 3/4)
video gate open: actual=38417 target=52000 reaimedTo=36000

Attempt 2 aimed at 44.0 and found 55.0, which is above the boundary and correctly refused. Attempt 3 aimed at 36.0 and took 38.417, the first sample above THAT aim. But 43.0 sits between them and covers the boundary perfectly well, so the epoch opened 4.6 s further back than the source required, and the run is offset from the plan by that much extra.

Why it matters now

Until #418 the offset was invisible: the clock claimed the target either way. With the axis published honestly, this is the difference between a resume landing at 39.4 s and at 43.0 s for a viewer who asked for 53 s. The depth of the re-aim is the landing error, one for one.

Shape of a fix

The scan from the re-aimed position up to the boundary already reads every packet in between, so the covering sample is passing through the loop that currently opens on the first one it sees. Remembering the last sync sample at or below the boundary and opening there costs one extra seek and no extra reading. The escalation ladder itself is fine; it is the choice within an attempt that is wrong.

Note the tolerance rule from #408 still applies at the top end (a container index entry is a decode timestamp while the gate judges presentation time, so a well-formed B-pyramid legitimately presents its keyframe a reorder delay above the boundary and must not pay a second seek for it).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions