fix artwork race, chat-only disposal, and stall recovery in pip
- Fix Now Playing artwork: controller's mediaInfo field is now mutable so setMediaInfo updates it before loadUrl sends to native. Bumps fork which also merges artworkUrl on the native side as defense-in-depth. - Pause native player in chat-only mode via showVideo reaction. Cancels latency/stall timers and aborts in-flight loads when video is hidden. - Remove PiP-specific stall recovery path — light refresh preserves the PiP window, so PiP can use the same escalation ladder as normal play. - Fix stale _initInFlight clobber on generation mismatch early-return. - Reset _streamQualityIndex on refresh to avoid stale quality label. - Thread-safe hasActivePlayers on SharedPlayerManager (fork). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
d8dad59ade
commit
e4de57e0b2
2 changed files with 29 additions and 15 deletions
|
|
@ -69,6 +69,7 @@ abstract class NativeVideoStoreBase with Store implements VideoPlayerInterface {
|
|||
|
||||
final _pip = SimplePip();
|
||||
ReactionDisposer? _disposeAndroidAutoPipReaction;
|
||||
ReactionDisposer? _disposeVideoModeReaction;
|
||||
|
||||
StreamSubscription<bool>? _pipSub;
|
||||
StreamSubscription<List<NativeVideoPlayerQuality>>? _qualitiesSub;
|
||||
|
|
@ -133,6 +134,23 @@ abstract class NativeVideoStoreBase with Store implements VideoPlayerInterface {
|
|||
updateStreamInfo();
|
||||
_initPlayer();
|
||||
|
||||
// Pause and release resources in chat-only mode so the AVPlayer
|
||||
// isn't decoding/buffering while the video UI is hidden.
|
||||
_disposeVideoModeReaction = reaction(
|
||||
(_) => settingsStore.showVideo,
|
||||
(showVideo) {
|
||||
if (!showVideo) {
|
||||
_controller?.pause();
|
||||
_userPaused = true;
|
||||
_latencyTimer?.cancel();
|
||||
_stallRecoveryTimer?.cancel();
|
||||
_initRetryTimer?.cancel();
|
||||
} else {
|
||||
handleRefresh();
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
if (Platform.isAndroid) {
|
||||
_disposeAndroidAutoPipReaction = autorun((_) async {
|
||||
if (settingsStore.showVideo && await SimplePip.isAutoPipAvailable) {
|
||||
|
|
@ -181,7 +199,8 @@ abstract class NativeVideoStoreBase with Store implements VideoPlayerInterface {
|
|||
|
||||
await _controller!.initialize();
|
||||
if (_disposed || generation != _initGeneration) {
|
||||
_initInFlight = false;
|
||||
// Don't reset _initInFlight here — a newer generation's _initPlayer
|
||||
// may already own the flag. Only the current generation should touch it.
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -305,11 +324,15 @@ abstract class NativeVideoStoreBase with Store implements VideoPlayerInterface {
|
|||
}
|
||||
}
|
||||
|
||||
if (_disposed || generation != _initGeneration) return;
|
||||
if (_disposed || generation != _initGeneration || !settingsStore.showVideo) {
|
||||
return;
|
||||
}
|
||||
_hlsUrl = twitchGqlApi.buildHlsUrl(login: userLogin, token: token);
|
||||
|
||||
await _controller!.loadUrl(url: _hlsUrl!);
|
||||
if (_disposed || generation != _initGeneration) return;
|
||||
if (_disposed || generation != _initGeneration || !settingsStore.showVideo) {
|
||||
return;
|
||||
}
|
||||
await _controller!.configureForLivePlayback();
|
||||
_startLatencyPolling();
|
||||
}
|
||||
|
|
@ -452,17 +475,6 @@ abstract class NativeVideoStoreBase with Store implements VideoPlayerInterface {
|
|||
|
||||
_stallRecoveryAttempt++;
|
||||
|
||||
// During PiP, only attempt light recovery (seek to live edge).
|
||||
// Heavy recovery (handleRefresh) disposes the controller, killing PiP.
|
||||
if (_isInPipMode) {
|
||||
if (_stallRecoveryAttempt > _maxRefreshAttempts) return;
|
||||
debugPrint('NativeVideoStore: stall in PiP, seeking to live edge');
|
||||
_controller?.seekToLiveEdge();
|
||||
_controller?.play();
|
||||
_startStallRecoveryTimer();
|
||||
return;
|
||||
}
|
||||
|
||||
if (_stallRecoveryAttempt <= 1) {
|
||||
// Light recovery: seek to live edge and resume.
|
||||
debugPrint('NativeVideoStore: stall detected, seeking to live edge');
|
||||
|
|
@ -642,6 +654,7 @@ abstract class NativeVideoStoreBase with Store implements VideoPlayerInterface {
|
|||
_pendingQualityIndex = null;
|
||||
_isQualitySwitching = false;
|
||||
_isAudioOnlyMode = false;
|
||||
_streamQualityIndex = 0;
|
||||
_latency = null;
|
||||
_availableStreamQualities = [];
|
||||
_qualityObjects = [];
|
||||
|
|
@ -866,6 +879,7 @@ abstract class NativeVideoStoreBase with Store implements VideoPlayerInterface {
|
|||
_pipSub?.cancel();
|
||||
_qualitiesSub?.cancel();
|
||||
_disposeAndroidAutoPipReaction?.call();
|
||||
_disposeVideoModeReaction?.call();
|
||||
|
||||
_controller?.removeActivityListener(_handleActivityEvent);
|
||||
_controller?.dispose();
|
||||
|
|
|
|||
|
|
@ -102,7 +102,7 @@ packages:
|
|||
description:
|
||||
path: "."
|
||||
ref: frosty-patches
|
||||
resolved-ref: e5986defd01033cda19421d3a70d59f492ff4869
|
||||
resolved-ref: "7ec3ba30f3506fbd727a8348c122bb8380fc5f88"
|
||||
url: "https://github.com/tommyxchow/flutter-native-video-player.git"
|
||||
source: git
|
||||
version: "0.4.11"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue