fix: restore iOS inline player after PiP closes
This commit is contained in:
@@ -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 }
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user