diff --git a/ios/Runner/MpvPlayer/MpvPlayerCore.swift b/ios/Runner/MpvPlayer/MpvPlayerCore.swift index 7b4295f1..0320df91 100644 --- a/ios/Runner/MpvPlayer/MpvPlayerCore.swift +++ b/ios/Runner/MpvPlayer/MpvPlayerCore.swift @@ -12,6 +12,7 @@ class MpvPlayerCore: MpvPlayerCoreBase { private weak var window: UIWindow? private var mainBlankView: UIView? private var isVisible = false + private var isDisposed = false var isPipStarting = false @@ -317,6 +318,19 @@ class MpvPlayerCore: MpvPlayerCoreBase { #endif func dispose() { + // Guard double-dispose: the plugin calls dispose() then drops the + // strong ref, which fires deinit → dispose() again. The second call + // would re-enter and crash on weak-ref formation during dealloc. + guard !isDisposed else { return } + isDisposed = true + + // Reset the HDMI mode hint synchronously while self is still alive + // and on main. An async-to-main dispatch here would be drained after + // dealloc (the plugin sets playerCore = nil right after this call + // returns), leaving the link stuck at the last clip's refresh rate. + updateDisplayCriteria( + doviProfile: 0, doviLevel: 0, fps: 0, width: 0, height: 0, sigPeak: 0) + NotificationCenter.default.removeObserver(self) #if os(iOS) ExternalDisplayManager.shared.detach(core: self) diff --git a/shared/apple/MpvPlayer/MpvPlayerCoreBase.swift b/shared/apple/MpvPlayer/MpvPlayerCoreBase.swift index 12b40bfb..77dc0514 100644 --- a/shared/apple/MpvPlayer/MpvPlayerCoreBase.swift +++ b/shared/apple/MpvPlayer/MpvPlayerCoreBase.swift @@ -439,10 +439,6 @@ class MpvPlayerCoreBase: NSObject { cachedContainerFps = 0 cachedLastSigPeak = 0 cacheLock.unlock() - DispatchQueue.main.async { [weak self] in - self?.updateDisplayCriteria( - doviProfile: 0, doviLevel: 0, fps: 0, width: 0, height: 0, sigPeak: 0) - } let mpvHandle = mpv mpv = nil @@ -477,6 +473,12 @@ class MpvPlayerCoreBase: NSObject { #if targetEnvironment(simulator) checkError(mpv_set_option_string(mpv, "avfoundation-composite-osd", "no")) checkError(mpv_set_option_string(mpv, "hwdec", "no")) + #elseif os(tvOS) + // tvOS HDR is HDMI mode switching, not EDR — an SDR sibling layer + // doesn't dim the video, so skip the per-frame CI composite that + // round-trips BT.2020/PQ through linear P3. + checkError(mpv_set_option_string(mpv, "avfoundation-composite-osd", "no")) + checkError(mpv_set_option_string(mpv, "hwdec", "videotoolbox")) #else checkError(mpv_set_option_string(mpv, "avfoundation-composite-osd", "yes")) checkError(mpv_set_option_string(mpv, "hwdec", "videotoolbox"))