Skip to content

fix(vod): open the restart scan-forward gate on keyframe pts and recover a gate starved to EOF#201

Merged
superuser404notfound merged 1 commit into
mainfrom
fix/ae169-r3-pregate-pts-gate
Jul 23, 2026
Merged

fix(vod): open the restart scan-forward gate on keyframe pts and recover a gate starved to EOF#201
superuser404notfound merged 1 commit into
mainfrom
fix/ae169-r3-pregate-pts-gate

Conversation

@superuser404notfound

Copy link
Copy Markdown
Owner

Problem (AetherEngine#169, round 3)

rrgomes' 5.19.1 retest showed the forward-wait escalation working (restarted=true on every tail miss), but the final segment was never produced by any pump, under any anchoring. The most diagnostic lifecycle: a producer anchored at the tail segment seeks to its advertised start (target=2878501), drops 200 packets waiting for a keyframe, and exits at EOF with packetsWritten=0.

Root cause

The restart scan-forward gate compared packet dts against a plan-boundary PTS (segmentPlan[baseIndex].startPts, a Cues timestamp). Under B-frame reorder a keyframe's dts sits a reorder delay below its own pts, so the gate dropped the exact IRAP the restart seeked for. This is the same defect class the #92 cutter fix removed from segment cutting; the pregate kept the dts comparison. Mid-file the next IRAP rescues the miss one GOP late (subtle content misalignment); at the file tail there is no next IRAP, so the unbounded VOD gate starved to EOF, matching the trace exactly (dropped=200 lastDts=2880419 isKey=false target=2878501, pump reason=eof packetsWritten=0).

Fix (three layers)

  1. pts-based gate (HLSSegmentProducer.videoGateTargetSatisfied): packets are judged by presentation time (dts fallback for NOPTS pts), so the anchor IRAP opens its own segment. Identical behavior for dts==pts sources.
  2. Starved-EOF re-anchor (shouldReanchorVODAfterGateStarvation + handleVODGateStarvationExit): a VOD pump whose gate starved to EOF proves no runtime keyframe exists at/after the targeted boundary (tail Cues drift or a mis-flagged tail IRAP). The engine re-anchors production on the segment of the last keyframe the gate dropped (bounded, VOD start at resume position with bridged audio (mp3→EAC3) never becomes ready; recovery re-anchor kills the muxer ("Cannot write moov atom before EAC3 packets parsed") with no escalation (#93 residual) #99-shaped gate), so the tail content gets produced and end-of-media completes through the 5.16.2 tail-park instead of dying at -12889 on a structurally unproducible segment.
  3. Readiness-gate liveness (rrgomes' secondary observation): the Loopback-HLS playback wedges under rapid repeated seeks; recovered clock diverges from the decoded frame #35/Loopback-HLS: the final segment of a long 4K DV title never completes at EOM — AVPlayer parks ~0.1s from the end (WaitingToMinimizeStalls) → -12889; resuming into the tail can't start a master #169 data-wait now consults pump liveness (currentProducerFinished, restart-in-flight aware). Production that already exited with nothing served fails over immediately instead of riding 8 rounds (24 s) behind a "still producing over a slow link" message describing the opposite of reality.

Verification

  • New Issue169GateStarvationTests (7) + StartupReadinessGateTests (+2), TDD red-to-green
  • Full suite: 975 tests / 160 suites green
  • -strict-concurrency=complete clean, tvOS Simulator build green

🤖 Generated with Claude Code

https://claude.ai/code/session_01NAPCg4tCDdSqkK6tPkNptw

…ver a gate starved to EOF (#169)

The restart gate compared packet dts against a plan-boundary PTS
(segmentPlan[baseIndex].startPts). Under B-frame reorder a keyframe's dts
sits a reorder delay below its own pts, so the gate dropped the exact IRAP
the restart seeked for (the defect class the #92 cutter fix removed from
segment cutting). Mid-file the next IRAP rescued the miss one GOP late; at
the file tail there is no next IRAP, so the unbounded VOD gate dropped
every remaining packet to EOF and the pump exited with packetsWritten=0
(rrgomes' seg719 trace: dropped=200, lastDts past target, isKey never true).

Three layers:

- The gate now judges packets by presentation time (videoGateTargetSatisfied,
  pts with dts fallback), so the anchor IRAP opens its own segment instead of
  being dropped into the previous one.
- A VOD pump that still starves its gate to EOF (no runtime keyframe at or
  after the targeted plan boundary, the structurally unproducible tail
  segment) re-anchors production on the segment of the last keyframe the
  gate dropped, bounded by a #99-shaped revive gate, so the tail content
  gets produced and end-of-media completes through the tail-park.
- The startup readiness gate's data-wait consults pump liveness: production
  that already exited with nothing served fails over immediately instead of
  riding 8 rounds (24 s) of false hope behind a message describing the
  opposite of reality.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NAPCg4tCDdSqkK6tPkNptw
@superuser404notfound
superuser404notfound merged commit 0138d38 into main Jul 23, 2026
3 checks passed
@superuser404notfound
superuser404notfound deleted the fix/ae169-r3-pregate-pts-gate branch July 23, 2026 04:23
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