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.
This commit is contained in:
Arkadiusz Fal
2026-10-31 11:11:39 +01:00
parent 29163b7989
commit c44edd23e6

View File

@@ -861,6 +861,9 @@ final class MPVRenderView: UIView {
private func startDisplayLink() { private func startDisplayLink() {
MPVLogging.logDisplayLink("creating", targetFPS: videoFPS) MPVLogging.logDisplayLink("creating", targetFPS: videoFPS)
displayLink = CADisplayLink(target: self, selector: #selector(displayLinkFired)) 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() updateDisplayLinkFrameRate()
displayLink?.add(to: .main, forMode: .common) displayLink?.add(to: .main, forMode: .common)
MPVLogging.logDisplayLink("started", isPaused: displayLink?.isPaused, targetFPS: videoFPS) MPVLogging.logDisplayLink("started", isPaused: displayLink?.isPaused, targetFPS: videoFPS)
@@ -921,6 +924,16 @@ final class MPVRenderView: UIView {
return 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 displayLink.isPaused = false
MPVLogging.logDisplayLink("resumed", isPaused: false, reason: "resumeRendering") MPVLogging.logDisplayLink("resumed", isPaused: false, reason: "resumeRendering")
LoggingService.shared.debug("MPV RenderView: displayLink resumed", category: .mpv) LoggingService.shared.debug("MPV RenderView: displayLink resumed", category: .mpv)