Skip to content

Wrap-escape containment: unbounded inferred sample duration, discarded discontinuity-scale fold runs, and a backpressure park that waits on an unadvertisable index #369

Description

@tschuegy

Context

Companion to the sequential chunk-seam rebase issue (#368): even with the rebase in place, a timestamp leap that escapes it (corrupt stream, a jump inside one chunk, future regressions) currently turns the session into a 4-minute zombie instead of failing fast. Three independent containment gaps, all observed in one device trace (AetherEngine 6.25.1, tvOS 26.6, sequential-origin Xtream timeshift):

1. An inferred sample duration longer than a discontinuity is accepted

resolveVideoSampleDuration returns nextDts − dts unbounded. Across a 2^33 wrap the look-behind finalize computes 8 226 410 192 ticks (~91 404 s @ 90 kHz) and hands it to movenc:

[mp4 @ 0x15eb6e700] [error] Application provided duration: 8226410192 in stream 0 is invalid

finalizeAndWriteVideo then discards the write return code, so the packet is silently lost and nothing in the log names the failure at ERROR level from our side.

2. Discontinuity-scale fold runs are discarded, disarming the #358 recovery arms

SegmentCache.noteFolded drops runs wider than maxFoldRunLength = 64 ("a jump this wide is a restart or a seek, not a fold"). The field case was precisely such a run — 312 indices:

[HLSSegmentProducer] #358 plan indices 2...313 folded into seg-314 (no IRAP reached their boundary)

With the counters at 0, both #358 recovery arms (foldedTargetDecision reanchor and the engine's unrecoverable-gap handler) stay disarmed, so the designed fold-once-reanchor / fold-twice-fail path never runs and the session limps on until the #65 wedge breaker trips minutes later.

3. The backpressure park can wait on an index the playlist can never advertise

The advance-path park waits for the consumer to fetch head − bufferAheadSegments, but on a sequential EVENT playlist the advertised frontier is fed by this same pump's finalize reports — zero-duration fold holes get no URI. After a fold-to-tail the park waits for a fetch of an index that will never appear:

[HLSSegmentProducer] #65 backpressure PARK (advance) head=374 target=364 cacheTarget=54 highStored=61 cached=62 parked=12s (no playback progress)

The consumer meanwhile advances (cacheTarget 54→60, rendered clock 208→243 s) until it drains the listed window and stalls. Parking on one's own advertised frontier is a structural self-deadlock.

Proposed containment (PR follows)

Deliberately out of scope: re-latching OutputTimestampSanitizer after a huge jump. movenc itself latches monotonicity once a wrapped packet is accepted, so a sanitizer reset would only convert garbage timestamps into rejected writes; with the rebase (#368) the sanitizer never sees the leap, and without it the fold-count containment above fails the session cleanly.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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