fix: iOS OOM during PiP playback cycles

Cancel leaked stream subscriptions in video player dispose and flush
sample buffer layer on PiP teardown/cleanup to release video frames.
This commit is contained in:
edde746
2026-03-04 17:34:30 +01:00
parent e6ee83d9c2
commit 3b351d7634
3 changed files with 17 additions and 3 deletions
@@ -245,10 +245,17 @@ class MpvPipController: NSObject {
}
pipController = nil
delegateHelper = nil
sampleBufferLayer.flushAndRemoveImage()
sampleBufferLayer.controlTimebase = nil
sampleBufferLayer.removeFromSuperlayer()
containerView?.removeFromSuperview()
containerView = nil
}
/// Flush enqueued sample buffers from the layer to free video frame memory
func flushLayer() {
sampleBufferLayer.flushAndRemoveImage()
}
}
// MARK: - PiP Delegate Helper (iOS 15+)
@@ -171,6 +171,7 @@ class MpvPlayerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, MpvPlayerD
playerCore?.isPipActive = false
isManualPipRequest = false
stopPipTimebaseSync()
pipController?.flushLayer()
_ = playerCore?.switchToGpuNextVO()
if pause { playerCore?.setProperty("pause", value: "yes") }
if notify { pipChannel?.invokeMethod("onPipChanged", arguments: false) }
+9 -3
View File
@@ -141,6 +141,9 @@ class VideoPlayerScreenState extends State<VideoPlayerScreen> with WidgetsBindin
StreamSubscription<void>? _playbackRestartSubscription;
StreamSubscription<void>? _backendSwitchedSubscription;
StreamSubscription<void>? _sleepTimerSubscription;
StreamSubscription<bool>? _mediaControlsPlayingSubscription;
StreamSubscription<Duration>? _mediaControlsPositionSubscription;
StreamSubscription<double>? _mediaControlsRateSubscription;
bool _isReplacingWithVideo = false; // Flag to skip orientation restoration during video-to-video navigation
bool _isDisposingForNavigation = false;
bool _waitingForExternalSubsTrackSelection = false;
@@ -799,12 +802,12 @@ class VideoPlayerScreenState extends State<VideoPlayerScreen> with WidgetsBindin
);
// Listen to playing state and update media controls
player!.streams.playing.listen((isPlaying) {
_mediaControlsPlayingSubscription = player!.streams.playing.listen((isPlaying) {
_updateMediaControlsPlaybackState();
});
// Listen to position updates for media controls and Discord
player!.streams.position.listen((position) {
_mediaControlsPositionSubscription = player!.streams.position.listen((position) {
_mediaControlsManager?.updatePlaybackState(
isPlaying: player!.state.playing,
position: position,
@@ -814,7 +817,7 @@ class VideoPlayerScreenState extends State<VideoPlayerScreen> with WidgetsBindin
});
// Listen to playback rate changes for Discord Rich Presence
player!.streams.rate.listen((rate) {
_mediaControlsRateSubscription = player!.streams.rate.listen((rate) {
DiscordRPCService.instance.updatePlaybackSpeed(rate);
});
@@ -1740,6 +1743,9 @@ class VideoPlayerScreenState extends State<VideoPlayerScreen> with WidgetsBindin
_playbackRestartSubscription?.cancel();
_backendSwitchedSubscription?.cancel();
_sleepTimerSubscription?.cancel();
_mediaControlsPlayingSubscription?.cancel();
_mediaControlsPositionSubscription?.cancel();
_mediaControlsRateSubscription?.cancel();
// Cancel auto-play timer
_autoPlayTimer?.cancel();