fix: restore iOS inline player after PiP closes

This commit is contained in:
edde746
2026-03-06 11:35:25 +01:00
parent 7b3a8ad642
commit 581f665f58
2 changed files with 61 additions and 6 deletions
+5
View File
@@ -118,6 +118,11 @@ class MpvPlayerCore: MpvPlayerCoreBase {
)
}
/// Nudge mpv to present the current paused frame after switching back from PiP.
func forceDraw() {
command(["seek", "0", "relative+exact"])
}
override func updateEDRMode(sigPeak: Double) {
guard let metalLayer else { return }
+56 -6
View File
@@ -18,6 +18,8 @@ class MpvPlayerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, MpvPlayerD
private var autoPipEnabled = false
private var isManualPipRequest = false
private var pipTimebaseSyncTimer: Timer?
private var pendingInlineRestoreAfterPip = false
private var sceneActivationObserverRegistered = false
// MARK: - FlutterPlugin Registration
@@ -95,6 +97,42 @@ class MpvPlayerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, MpvPlayerD
return controller
}
private func registerSceneActivationObserver() {
guard !sceneActivationObserverRegistered else { return }
NotificationCenter.default.addObserver(
self,
selector: #selector(sceneDidActivate),
name: UIScene.didActivateNotification,
object: nil
)
sceneActivationObserverRegistered = true
}
private func unregisterSceneActivationObserver() {
guard sceneActivationObserverRegistered else { return }
NotificationCenter.default.removeObserver(self, name: UIScene.didActivateNotification, object: nil)
sceneActivationObserverRegistered = false
}
private var isSceneActive: Bool {
UIApplication.shared.connectedScenes.contains { $0.activationState == .foregroundActive }
}
private func restoreInlinePlayerAfterPip() {
guard pendingInlineRestoreAfterPip,
let playerCore = playerCore,
!playerCore.isPipActive,
!playerCore.isPipStarting else { return }
print("[MpvPlayerPlugin] Restoring inline player after PiP")
playerCore.setVisible(true)
playerCore.updateFrame()
if playerCore.isPaused {
playerCore.forceDraw()
}
pendingInlineRestoreAfterPip = false
}
private func handlePipCall(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
switch call.method {
case "isSupported":
@@ -111,11 +149,8 @@ class MpvPlayerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, MpvPlayerD
if let pc = playerCore {
pip.warmLayer(currentTime: pc.timePos, isPlaying: !pc.isPaused)
}
NotificationCenter.default.removeObserver(self, name: UIScene.didActivateNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(sceneDidActivate), name: UIScene.didActivateNotification, object: nil)
} else {
pipController?.setAutoStart(false)
NotificationCenter.default.removeObserver(self, name: UIScene.didActivateNotification, object: nil)
}
}
result(nil)
@@ -131,6 +166,7 @@ class MpvPlayerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, MpvPlayerD
guard let playerCore = playerCore else { return nil }
let pip = ensurePipController()
guard playerCore.switchToPipVO(layerPtr: pip.layerPointer) else { return nil }
pendingInlineRestoreAfterPip = false
playerCore.isPipStarting = true
pip.pushBlankFrame()
pip.syncTimebase(currentTime: playerCore.timePos, isPlaying: !playerCore.isPaused)
@@ -172,13 +208,23 @@ class MpvPlayerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, MpvPlayerD
isManualPipRequest = false
stopPipTimebaseSync()
pipController?.flushLayer()
_ = playerCore?.switchToGpuNextVO()
let restoredInlineVO = playerCore?.switchToGpuNextVO() ?? false
if pause { playerCore?.setProperty("pause", value: "yes") }
pendingInlineRestoreAfterPip = restoredInlineVO
if pendingInlineRestoreAfterPip {
if isSceneActive {
restoreInlinePlayerAfterPip()
} else {
print("[MpvPlayerPlugin] Deferring inline restore until scene activation")
}
}
if notify { pipChannel?.invokeMethod("onPipChanged", arguments: false) }
}
/// Scene became active re-warm the layer so auto-PiP stays possible
/// Scene became active restore inline playback if needed and re-warm the
/// sample-buffer layer so future auto-PiP remains possible.
@objc private func sceneDidActivate() {
restoreInlinePlayerAfterPip()
if autoPipEnabled, let pip = pipController, let pc = playerCore, !pc.isPipActive {
pip.warmLayer(currentTime: pc.timePos, isPlaying: !pc.isPaused)
}
@@ -216,6 +262,7 @@ class MpvPlayerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, MpvPlayerD
}
if self.playerCore?.isInitialized == true {
self.registerSceneActivationObserver()
result(true)
return
}
@@ -234,6 +281,7 @@ class MpvPlayerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, MpvPlayerD
}
self.playerCore = core
self.registerSceneActivationObserver()
core.setVisible(false)
result(true)
}
@@ -245,7 +293,8 @@ class MpvPlayerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, MpvPlayerD
self.pipController?.teardown()
self.pipController = nil
self.autoPipEnabled = false
NotificationCenter.default.removeObserver(self, name: UIScene.didActivateNotification, object: nil)
self.pendingInlineRestoreAfterPip = false
self.unregisterSceneActivationObserver()
self.stopPipTimebaseSync()
self.playerCore?.dispose()
self.playerCore = nil
@@ -384,6 +433,7 @@ extension MpvPlayerPlugin: MpvPipDelegate {
func pipDidStart() {
playerCore?.isPipStarting = false
playerCore?.isPipActive = true
pendingInlineRestoreAfterPip = false
pipChannel?.invokeMethod("onPipChanged", arguments: true)
syncPipTimebase()
startPipTimebaseSync()