fix(native): bound cross-platform lifecycle ownership

This commit is contained in:
edde746
2026-07-24 03:56:40 +02:00
parent 09656fa4d3
commit 9f2e050797
98 changed files with 12908 additions and 1599 deletions
+22 -2
View File
@@ -13,6 +13,7 @@ import 'package:sentry_flutter/sentry_flutter.dart';
import '../mpv/mpv.dart';
import '../mpv/player/platform/player_android.dart';
import '../mpv/player/player_native.dart';
import '../services/scrub_preview_source.dart';
import '../media/media_backend.dart';
@@ -122,6 +123,16 @@ part 'video_player/parts/watch_together.dart';
final WakelockController _wakelockController = WakelockController();
/// Whether an in-place source reload may start the replacement media.
///
/// Reloading a paused player must not manufacture a new play intent. Watch
/// Together and explicit paused starts keep owning the eventual resume.
bool shouldAutoStartReloadedMedia({
required bool wasPlayingBeforeReload,
required bool watchTogetherOwnsStart,
required bool startPaused,
}) => wasPlayingBeforeReload && !watchTogetherOwnsStart && !startPaused;
/// The in-place media-source transitions a [VideoPlayerScreenState] can run.
/// They are mutually exclusive by construction — entry points bail while a
/// transition is in flight.
@@ -279,6 +290,7 @@ class VideoPlayerScreenState extends State<VideoPlayerScreen> with WidgetsBindin
static bool isNavigationActive(VideoPlayerLaunchIdentity identity) => _activeRouteGuard.blocks(identity);
Player? player;
Player? _bootstrapPlayer;
VideoVolumeController? _volumeController;
bool _isPlayerInitialized = false;
String? _playerInitializationError;
@@ -884,6 +896,9 @@ class VideoPlayerScreenState extends State<VideoPlayerScreen> with WidgetsBindin
if (identical(player, attemptPlayer)) {
player = null;
}
if (identical(_bootstrapPlayer, attemptPlayer)) {
_bootstrapPlayer = null;
}
try {
await _tearDownFailedPlayerAttempt(attemptPlayer);
} catch (e, st) {
@@ -955,6 +970,9 @@ class VideoPlayerScreenState extends State<VideoPlayerScreen> with WidgetsBindin
final currentPlayer = Player(useExoPlayer: useExoPlayer);
attemptPlayer = currentPlayer;
if (!mounted || generation != _playerInitializationGeneration) return;
if (currentPlayer is PlayerNative && currentPlayer.requiresProvisionalTextureSurface) {
setState(() => _bootstrapPlayer = currentPlayer);
}
if (Platform.isAndroid && useExoPlayer) {
await currentPlayer.setLogLevel(debugLoggingEnabled ? 'v' : 'warn');
if (!mounted || generation != _playerInitializationGeneration) return;
@@ -1194,6 +1212,7 @@ class VideoPlayerScreenState extends State<VideoPlayerScreen> with WidgetsBindin
if (mounted) {
setState(() {
_isPlayerInitialized = true;
_bootstrapPlayer = null;
});
// Restart sleep timer if we're starting a new playback session
@@ -1548,8 +1567,9 @@ class VideoPlayerScreenState extends State<VideoPlayerScreen> with WidgetsBindin
final volumeController = _volumeController;
_volumeController = null;
volumeController?.dispose();
final playerToDispose = player;
final playerToDispose = player ?? _bootstrapPlayer;
player = null;
_bootstrapPlayer = null;
if (playerToDispose != null) {
// Keep the native display mode (tvOS HDMI criteria) across a
// player→player handoff; the replacement screen primes its own.
@@ -1866,7 +1886,7 @@ class VideoPlayerScreenState extends State<VideoPlayerScreen> with WidgetsBindin
? _buildVideoPlayer(sheetContext)
: (_playerInitializationError != null
? _buildInitializationError(_playerInitializationError!)
: _buildLoadingSpinner()),
: _buildPlayerInitializationSurface()),
),
),
);