fix(player): 修复清晰度切换闪屏与还原黑屏

This commit is contained in:
Vicky authored and trae-solo-agent committed 2026-10-28 16:36:55 +08:00
1 parent 43e60e29ec
commit 85dedf20fa
8 files changed
+249 -296

No files matched your search

@@ -29,6 +29,7 @@ class PlayerVideoSurface extends StatefulWidget {
this.debugIndex,
this.showFallbackBackground = true,
this.onVideoVisible,
this.resumeSignal = 0,
});
final VideoPlayerController? controller;
@@ -60,6 +61,10 @@ class PlayerVideoSurface extends StatefulWidget {
final bool theater;
final VoidCallback? onVideoVisible;
/// iOS 画中画还原等场景的恢复信号:数值变化时回退到占位并重新等首帧,
/// 避免纹理被系统回收后显示黑屏(封面占位 → 新帧淡入)。
final int resumeSignal;
@override
State<PlayerVideoSurface> createState() => _PlayerVideoSurfaceState();
}
@@ -76,6 +81,11 @@ class _PlayerVideoSurfaceState extends State<PlayerVideoSurface>
bool _transitioning = false;
bool _videoVisible = false;
bool _videoVisibleNotified = false;
/// 清晰度切换时新源 seek 后的进度;position 越过它才认定纹理已有画面。
Duration? _confirmBasePosition;
/// 画中画还原等场景的首帧确认下限时刻:position 可能一拍就过基准,
/// 但纹理出帧滞后于进度观测,淡入前强制等待一小段真实解码时间。
DateTime? _confirmNotBefore;
String? _lastLayerState;
@override
@@ -99,6 +109,13 @@ class _PlayerVideoSurfaceState extends State<PlayerVideoSurface>
@override
void didUpdateWidget(covariant PlayerVideoSurface oldWidget) {
super.didUpdateWidget(oldWidget);
if (oldWidget.resumeSignal != widget.resumeSignal) {
_logSlot('pip-resume-reset', widget.controller?.value);
_resetForControllerChange();
_confirmNotBefore = DateTime.now().add(const Duration(milliseconds: 450));
setState(() {});
if (oldWidget.controller == widget.controller) return;
}
if (oldWidget.controller != widget.controller) {
_logSlot('controller-change-before', oldWidget.controller?.value);
_detachController(oldWidget.controller);
@@ -261,6 +278,16 @@ class _PlayerVideoSurfaceState extends State<PlayerVideoSurface>
return false;
}
if (widget.warmFrameAvailable || !widget.waitForPlaybackFrame) return true;
// 清晰度切换:新源 seek 完成后 position 立即大于 0,但不代表纹理已有
// 画面;等 position 越过切换时刻的值(解码循环已在出帧)再确认,避免闪黑。
final base = _confirmBasePosition;
if (base != null) {
final notBefore = _confirmNotBefore;
if (notBefore != null && DateTime.now().isBefore(notBefore)) {
return false;
}
return value.position > base || value.isCompleted;
}
if (value.position > Duration.zero || value.isCompleted) return true;
return _playbackStarted && !value.isPlaying && !value.isBuffering;
}
@@ -321,6 +348,8 @@ class _PlayerVideoSurfaceState extends State<PlayerVideoSurface>
_videoVisible = false;
_videoVisibleNotified = false;
_lastLayerState = null;
// 记录新源切换时刻的进度,供首帧确认判断 position 是否已前进。
_confirmBasePosition = widget.controller?.value.position;
}
void _notifyVideoVisible() {