Skip to content

fix(dash): un-latch track presence so the live edge survives a mid-session track death#14

Merged
ntt0601zcoder merged 1 commit into
mainfrom
fix/dash-track-death
Jun 13, 2026
Merged

fix(dash): un-latch track presence so the live edge survives a mid-session track death#14
ntt0601zcoder merged 1 commit into
mainfrom
fix/dash-track-death

Conversation

@ntt0601zcoder

Copy link
Copy Markdown
Owner

Problem (audit A-8, HIGH)

The DASH packager derived haveVideo/haveAudio from videoInit/audioInit, which are built once and never cleared. When one elementary stream stops mid-session — an encoder fault, or a failover swap to a single-track feed while the publisher keeps running by design — the presence flag stays latched true. buildCutDecision's V/A duration coupling then holds every cut waiting for the dead track's frames (Ok=false on every 50 ms tick), so:

  • the live edge freezes permanently for all DASH viewers,
  • the FrameQueue saturates maxQueueSpanMs and overflow-drops every incoming frame,
  • only a manual restart recovers — and HLS on the same stream keeps working, masking the failure.

The symmetric video-death case is unreachable too: cutAudioOnly requires !haveVideo, which never becomes false while latched.

Fix

Derive track presence from arrival liveness instead of init existence:

  • handleH264 / handleAAC stamp lastVideoFrameAt / lastAudioFrameAt on every accepted frame.
  • New liveTrackPresence(now) declares a track dead once the stream is StateLive and that track has both an empty queue and no arrival within trackLossTimeout (6 s).
  • Down-grading is gated to StateLive only — WaitingForPairing (pairing handshake) and SessionBoundary (post-reset stale timestamps) keep init-presence so neither is disturbed.

Audio death → haveAudio=false → coupling skipped (buildCutDecision's audio block already gates on haveAudio) → video cuts resume. Video death → haveVideo=falsecutAudioOnly engages. Presence flips back automatically when the track resumes (handlers re-stamp), and the wallclock-anchored tfdt lands the resumed segment at the live edge with no extra re-anchoring.

Test

TestLiveTrackPresence_TrackDeath covers: audio-dead/video-survives, video-dead/audio-survives, still-draining-queue (not dead), recent-frames (alive), waiting-for-pairing (no down-grade), and genuine single-track stream.

Package: go test -race ./internal/publisher/dash/ green, golangci-lint 0 issues, full go build ./... green.

…ssion track death

The DASH packager derived haveVideo/haveAudio from videoInit/audioInit,
which are built once and never cleared. When one elementary stream stopped
mid-session (encoder fault, or a failover swap to a single-track feed while
the publisher keeps running by design) the presence flag stayed true, so
buildCutDecision's V/A duration coupling held every cut waiting for the dead
track's frames — Ok=false on every tick. The live edge froze permanently for
all DASH viewers, the frame queue saturated and overflow-dropped, and only a
manual restart recovered it. HLS on the same stream kept working, masking it.

Derive presence from arrival liveness instead: handleH264/handleAAC stamp the
last-frame-arrival wallclock per track, and a new liveTrackPresence(now)
declares a track dead once the stream is Live and that track has both an empty
queue and no arrival within trackLossTimeout (6 s). Down-grading is gated to
StateLive only, so the pairing handshake and post-session-boundary stale
timestamps are untouched. Audio death then skips the coupling and video cuts
resume; video death routes to cutAudioOnly. Presence flips back automatically
when the track resumes, and the wallclock-anchored tfdt lands the resumed
segment at the live edge with no extra re-anchoring.

Test TestLiveTrackPresence_TrackDeath covers audio-dead, video-dead,
still-draining-queue, recent-frames, waiting-for-pairing (no down-grade), and
genuine single-track cases.
@codecov-commenter

Copy link
Copy Markdown

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@ntt0601zcoder ntt0601zcoder merged commit 4f27c66 into main Jun 13, 2026
4 checks passed
@ntt0601zcoder ntt0601zcoder deleted the fix/dash-track-death branch June 13, 2026 13:16
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.

2 participants