Suppress tvOS Now Playing while AirPlay/HomePod route is active

On tvOS, registering MPRemoteCommandCenter handlers makes the system
classify the app as a long-form video media app. When audio is routed
to AirPlay 2 endpoints (HomePods), the system then enforces a ~2s
look-ahead buffer in the AVAudioSession → AirPlay 2 pipe for
multi-speaker sync. The result is a 2-3 second audio drain on pause
and refill on resume.

The buffer lives downstream of mpv, so no mpv command (ao-reload,
seek-flush, audio-add) can flush it; AVAudioSession setCategory
overrides (mode/policy) and setActive(false)/setActive(true) cycles
are also ignored once the app is media-classified.

Workaround: detect the active audio route via routeChangeNotification
on tvOS. While AirPlay/HomePod is the output, suppress
MPNowPlayingInfoCenter publication and disable every MPRemoteCommand
so tvOS un-classifies us. When the route returns to local outputs,
republish the cached Now Playing info and reconfigure remote commands.
A latch defers all media integration until the audio session has been
activated at least once, so no commands are registered before the
route can be evaluated.

Trade-off: while playing to HomePods, the Control Center widget and
external Siri Remote play/pause are not available — but pause/resume
is responsive.
This commit is contained in:
Arkadiusz Fal
2026-05-09 14:18:17 +02:00
parent 9287f5906d
commit 42621b8193
2 changed files with 237 additions and 9 deletions

View File

@@ -1563,6 +1563,10 @@ final class PlayerService {
try session.setCategory(.playback, mode: .moviePlayback)
try session.setActive(true)
#if os(tvOS)
nowPlayingService.refreshSystemControlsForCurrentAudioRoute(reason: "audio session activation")
#endif
// Only register observer once
guard !hasRegisteredInterruptionObserver else { return }
hasRegisteredInterruptionObserver = true