14c5e7c11c
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>
51 lines
1.8 KiB
Plaintext
51 lines
1.8 KiB
Plaintext
<?xml version="1.0" encoding="UTF-8"?>
|
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
<plist version="1.0">
|
|
<dict>
|
|
<key>CADisableMinimumFrameDurationOnPhone</key>
|
|
<true/>
|
|
<key>GCSupportedGameControllers</key>
|
|
<array>
|
|
<dict>
|
|
<key>ProfileName</key>
|
|
<string>ExtendedGamepad</string>
|
|
</dict>
|
|
<dict>
|
|
<key>ProfileName</key>
|
|
<string>MicroGamepad</string>
|
|
</dict>
|
|
</array>
|
|
<key>NSBonjourServices</key>
|
|
<array>
|
|
<string>_punktfunk._udp</string>
|
|
</array>
|
|
<!-- Background keep-alive (opt-in, iOS/iPadOS): the ONLY sanctioned way to keep the long-lived
|
|
QUIC socket + pump-thread set alive while backgrounded is the audio background mode, backed
|
|
by the session's real, audible remote audio (AVAudioEngine keeps rendering). Video decode is
|
|
dropped; a bounded timer auto-disconnects. Never silence-as-keepalive (App Review 2.5.4).
|
|
tvOS ignores/tolerates the key; macOS is not gated by it. -->
|
|
<key>UIBackgroundModes</key>
|
|
<array>
|
|
<string>audio</string>
|
|
</array>
|
|
<!-- Live Activities (iOS/iPadOS): the Lock-Screen / Dynamic-Island session surface. Updated
|
|
locally (pushType nil) from the alive app process — no aps-environment. tvOS/macOS ignore it. -->
|
|
<key>NSSupportsLiveActivities</key>
|
|
<true/>
|
|
<!-- Deep links: punktfunk://connect/<host-uuid>[?launch=<GameEntry.id>]. Emitted by the
|
|
launcher widget and Siri/Shortcuts; routed by ContentView.onOpenURL into the existing
|
|
connect path. Shared across all three targets (tvOS/macOS accept it harmlessly). -->
|
|
<key>CFBundleURLTypes</key>
|
|
<array>
|
|
<dict>
|
|
<key>CFBundleURLName</key>
|
|
<string>io.unom.punktfunk.deeplink</string>
|
|
<key>CFBundleURLSchemes</key>
|
|
<array>
|
|
<string>punktfunk</string>
|
|
</array>
|
|
</dict>
|
|
</array>
|
|
</dict>
|
|
</plist>
|