From c44edd23e6f70084538cc2c05c99d7d3517b4e2c Mon Sep 17 00:00:00 2001 From: Arkadiusz Fal Date: Sat, 31 Oct 2026 11:11:39 +0100 Subject: [PATCH] Keep the display link paused while the app is in the background A TestFlight log (iOS 27.2, build 276) showed ~36% CPU on an unnamed thread for 85 minutes with playback paused in the background, heating the phone. A lock during a quick inactive/active bounce rebuilt the player container after the app had entered the background, and its resumeRendering() un-paused the display link there; heartbeats showed displayLink=running@60 inBackground=true for the whole period. An earlier identical rebuild in the foreground left background CPU at 0.4%. resumeRendering() now ignores calls in the background (unless PiP is rendering) and a display link created there starts paused; appDidBecomeActive resumes it. --- Yattee/Services/Player/MPV/MPVRenderView.swift | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/Yattee/Services/Player/MPV/MPVRenderView.swift b/Yattee/Services/Player/MPV/MPVRenderView.swift index 3c71d5f1..4253d9bb 100644 --- a/Yattee/Services/Player/MPV/MPVRenderView.swift +++ b/Yattee/Services/Player/MPV/MPVRenderView.swift @@ -861,6 +861,9 @@ final class MPVRenderView: UIView { private func startDisplayLink() { MPVLogging.logDisplayLink("creating", targetFPS: videoFPS) displayLink = CADisplayLink(target: self, selector: #selector(displayLinkFired)) + // Created in the background (view re-added during a lock): stay paused + // until appDidBecomeActive, as in resumeRendering. + displayLink?.isPaused = isInBackground && !isPiPActive && !captureFramesForPiP updateDisplayLinkFrameRate() displayLink?.add(to: .main, forMode: .common) MPVLogging.logDisplayLink("started", isPaused: displayLink?.isPaused, targetFPS: videoFPS) @@ -921,6 +924,16 @@ final class MPVRenderView: UIView { return } + // Layout can rebuild the player's container after the app entered the + // background (e.g. a lock during a quick inactive/active bounce), which + // calls this. The display link never fires there, but left running it + // matched a TestFlight report of ~36% CPU on an unnamed thread for as + // long as the app stayed alive. appDidBecomeActive resumes it. + guard !isInBackground || isPiPActive || captureFramesForPiP else { + LoggingService.shared.info("MPV RenderView: resumeRendering ignored in background", category: .mpv) + return + } + displayLink.isPaused = false MPVLogging.logDisplayLink("resumed", isPaused: false, reason: "resumeRendering") LoggingService.shared.debug("MPV RenderView: displayLink resumed", category: .mpv)