fix(native): bound cross-platform lifecycle ownership
This commit is contained in:
@@ -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()),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user