fix(player): 隔离播放资源所有权并修复黑屏

This commit is contained in:
Zhou Weidong committed 2026-10-26 17:03:19 +08:00
1 parent 54eb5a60a8
commit bb2c729a7e
9 files changed
+223 -25

No files matched your search

+42 -7
View File
@@ -90,7 +90,7 @@ class PlayerPage extends StatefulWidget {
this.wallet,
this.downloads,
this.handoff,
this.isolatePlaybackSession = false,
this.revealAfterFirstFrame = false,
});
final Drama drama;
@@ -118,7 +118,7 @@ class PlayerPage extends StatefulWidget {
final WalletController? wallet;
final DownloadController? downloads;
final PlayerPageHandoff? handoff;
final bool isolatePlaybackSession;
final bool revealAfterFirstFrame;
@override
State<PlayerPage> createState() => _PlayerPageState();
@@ -141,6 +141,7 @@ class _PlayerPageState extends State<PlayerPage>
DateTime? _swipeStartedAt;
bool _swipePrewarmCaptured = false;
bool _presentationActive = false;
late bool _firstFrameReady;
bool _leavingHandoff = false;
bool _preparingFullPlayer = false;
final Set<String> _precachedPosterUrls = {};
@@ -152,6 +153,7 @@ class _PlayerPageState extends State<PlayerPage>
@override
void initState() {
super.initState();
_firstFrameReady = !widget.revealAfterFirstFrame;
final handoff = widget.handoff;
logic = PlayerBinding().create(
history: context.read<WatchHistoryRepository>(),
@@ -169,12 +171,18 @@ class _PlayerPageState extends State<PlayerPage>
WidgetsBinding.instance.addObserver(this);
logic.start(
factory: context.read<PlayerFactory>(),
coordinator: handoff == null && !widget.isolatePlaybackSession
coordinator: widget.embedded
? context.read<PlaybackCoordinator>()
: PlaybackCoordinator(),
initialUserWantsPlay: handoff?.sourceUserWantsPlay ?? true,
);
_pageController = logic.pages;
if (kDebugMode && widget.revealAfterFirstFrame) {
debugPrint(
'[player-entry] phase=waiting-first-frame '
'session=${identityHashCode(logic.session)}',
);
}
}
@override
@@ -594,6 +602,7 @@ class _PlayerPageState extends State<PlayerPage>
PlayerRenderStack(
pageController: _pageController,
slots: _renderSlots(indices),
onCurrentVideoVisible: _handleCurrentVideoVisible,
),
pageLayer,
],
@@ -660,16 +669,37 @@ class _PlayerPageState extends State<PlayerPage>
),
),
);
if (widget.handoff == null) return player;
final revealContent =
!widget.revealAfterFirstFrame ||
_firstFrameReady ||
logic.session.isLocked ||
logic.ticketError != null ||
logic.blockedEpisodeId != null ||
logic.session.error != null;
final presentedPlayer = widget.revealAfterFirstFrame
? Opacity(opacity: revealContent ? 1 : 0, child: player)
: player;
if (widget.handoff == null) return presentedPlayer;
return PopScope<void>(
canPop: _leavingHandoff,
onPopInvokedWithResult: (didPop, _) {
if (!didPop) unawaited(_leaveHandoff(context));
},
child: player,
child: presentedPlayer,
);
}
void _handleCurrentVideoVisible() {
if (!mounted || _firstFrameReady) return;
setState(() => _firstFrameReady = true);
if (kDebugMode) {
debugPrint(
'[player-entry] phase=ready '
'session=${identityHashCode(logic.session)}',
);
}
}
void _openCurrentMore() {
final entry = logic.queue.entryAt(logic.session.currentIndex);
_openMore(context, entry.episode);
@@ -1176,13 +1206,18 @@ class _PlayerPageState extends State<PlayerPage>
_logFullPlayerRoute('present');
setState(() => _presentationActive = true);
final route = Navigator.of(context).push<void>(
MaterialPageRoute<void>(
builder: (_) => PlayerPage(
PageRouteBuilder<void>(
opaque: false,
barrierDismissible: false,
transitionDuration: Duration.zero,
reverseTransitionDuration: Duration.zero,
pageBuilder: (_, _, _) => PlayerPage(
drama: seriesDrama,
initialIndex: episodeIndex,
handoff: handoff,
wallet: logic.wallet,
downloads: logic.downloads,
revealAfterFirstFrame: true,
),
),
);