Backgrounding a live session no longer freezes it when the user opts in: audio
keeps playing (UIBackgroundModes audio), the QUIC connection + pump stay live,
video decode is DROPPED, and a bounded timer auto-disconnects. Off by default.
- PunktfunkConnection.setVideoDropped/isVideoDropped: a tiny lock-guarded flag
both pumps read every iteration. StreamPump (stage-1), Stage2Pipeline (VT +
PyroWave) drain nextAU() for flow control but DISCARD the AU before any
VideoToolbox/Metal work — the crash/jetsam-safe seam (no GPU off-screen).
- SessionModel.enterBackground(timeoutMinutes:) / exitBackground(): set the drop
flag, mute the mic (privacy — SessionAudio.setMicMuted pauses the capture
engine), arm a DispatchSourceTimer that disconnect(deliberate:false)s on fire
(keeps host linger → fast late reconnect). exitBackground clears the flag and
requestKeyframe()s; the pump's freeze gate auto-arms on the resumed
frame-index gap so concealed frames are withheld until the IDR re-anchors.
disconnect() cancels the timer + clears isBackgrounded.
- ContentView scenePhase driver (iOS): .background+streaming+setting →
enterBackground; .active → exitBackground. scenePhase (not willResignActive)
so Control-Center/app-switcher peeks don't start the timer.
- Settings → General (iOS-only keepAliveSection): toggle + 1/5/10/30 timeout;
new keys backgroundKeepAlive (def off) / backgroundTimeoutMinutes (def 10).
- Info.plist: UIBackgroundModes [audio] + NSSupportsLiveActivities (for M3).
macOS swift build + swift test green (142 tests). The iOS-gated scenePhase
handler + settings section are not exercised by the macOS CI target (known §9
gap) — need on-glass verification (audio never gaps, video re-anchors <1s LAN,
timeout ends the session, phone-call audio-steal degrades gracefully).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>