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

@@ -15,6 +15,16 @@ class PlaybackCoordinator {
if (!valid()) return false;
if (_owner != session) {
final previous = _owner;
if (previous != null && previous.owner != session.owner) {
if (kDebugMode) {
debugPrint(
'[player-owner] phase=reject-coordinator-handoff '
'owner=${session.owner.name} '
'activeOwner=${previous.owner.name}',
);
}
return false;
}
await previous?._releasePlayers();
if (previous != null && previous.livePlayerCount != 0) return false;
_owner = null;
@@ -56,6 +66,10 @@ Future<void> _parkNeighbors(
Future<void> _placeNeighbor(PlaybackSession session, _PlayerSlot? slot) async {
if (slot == null || slot == session._active || slot.retiring) return;
if (slot.owner != session.owner) {
session._logOwnerRejection('park', slot);
return;
}
if (session._next == null && slot.index == session._nextIndex) {
session._next = slot;
return;
+48 -3
View File
@@ -17,6 +17,8 @@ typedef PlayerFactory = PlayerPort Function(Episode episode);
typedef PlayerPreparation =
Future<void> Function(Episode episode, PlayerPort player);
enum PlaybackSessionOwner { home, player }
/// Owns current plus previous/next frames; allocation stays serialized.
class PlaybackSession extends ChangeNotifier {
PlaybackSession({
@@ -27,6 +29,8 @@ class PlaybackSession extends ChangeNotifier {
this.activate,
PlaybackCoordinator? coordinator,
this.waitForFrame,
this.owner = PlaybackSessionOwner.player,
this.dramaIdAt,
}) : _coordinator = coordinator ?? PlaybackCoordinator();
final List<Episode> episodes;
@@ -39,6 +43,8 @@ class PlaybackSession extends ChangeNotifier {
/// Runs only when a slot becomes current (e.g. progress listeners).
final PlayerPreparation? activate;
final Future<void> Function()? waitForFrame;
final PlaybackSessionOwner owner;
final String? Function(int index)? dramaIdAt;
final PlaybackCoordinator _coordinator;
final Set<_PlayerSlot> _live = {};
_PlayerSlot? _active;
@@ -161,7 +167,12 @@ class PlaybackSession extends ChangeNotifier {
if (_live.length >= maxLivePlayers) {
throw StateError('Playback resource limit reached');
}
final slot = _PlayerSlot(index, factory(episodes[index]));
final slot = _PlayerSlot(
index,
factory(episodes[index]),
owner: owner,
dramaId: dramaIdAt?.call(index),
);
_live.add(slot);
slot.ready = () async {
try {
@@ -180,7 +191,19 @@ class PlaybackSession extends ChangeNotifier {
}
Future<void> _retire(_PlayerSlot? slot) async {
if (slot == null || !_live.contains(slot)) return;
if (slot == null) return;
if (slot.owner != owner) {
_logOwnerRejection('retire', slot);
return;
}
if (!_live.contains(slot)) return;
if (kDebugMode) {
debugPrint(
'[player-owner] phase=retire owner=${slot.owner.name} '
'drama=${slot.dramaId ?? '-'} episodeIndex=${slot.index} '
'controller=${_playerIdentity(slot.player)}',
);
}
slot.retiring = true;
final cleared =
_active == slot ||
@@ -460,6 +483,15 @@ class PlaybackSession extends ChangeNotifier {
if (!_closed) notifyListeners();
}
void _logOwnerRejection(String phase, _PlayerSlot slot) {
if (!kDebugMode) return;
debugPrint(
'[player-owner] phase=reject-$phase expected=${owner.name} '
'actual=${slot.owner.name} drama=${slot.dramaId ?? '-'} '
'episodeIndex=${slot.index} controller=${_playerIdentity(slot.player)}',
);
}
Future<void> close() async {
if (_closed) return;
_closed = true;
@@ -470,9 +502,16 @@ class PlaybackSession extends ChangeNotifier {
}
class _PlayerSlot {
_PlayerSlot(this.index, this.player);
_PlayerSlot(
this.index,
this.player, {
required this.owner,
required this.dramaId,
});
int index;
final PlayerPort player;
final PlaybackSessionOwner owner;
final String? dramaId;
late final Future<void> ready;
bool prepared = false;
bool activated = false;
@@ -481,3 +520,9 @@ class _PlayerSlot {
bool reusedWarm = false;
Object? failure;
}
String _playerIdentity(PlayerPort player) {
return player is VideoPlayerPort
? '${identityHashCode(player.controller)}'
: '${identityHashCode(player)}';
}