fix(player): 使用高清首帧并稳定切集占位
This commit is contained in:
1 parent
0cb0ab0613
commit
2fef7470e0
10 files changed
+258
-33
No files matched your search
@@ -5,6 +5,7 @@ import 'package:flutter/material.dart';
|
||||
import 'package:video_player/video_player.dart';
|
||||
|
||||
import '../../../core/theme/app_colors.dart';
|
||||
import 'player_opening_frame.dart';
|
||||
|
||||
const Duration _crossFadeDuration = Duration(milliseconds: 180);
|
||||
const double _errorHorizontal = 32;
|
||||
@@ -20,6 +21,9 @@ class PlayerVideoSurface extends StatefulWidget {
|
||||
this.placeholder,
|
||||
this.warmFrameAvailable = false,
|
||||
this.thumbnailReady = false,
|
||||
this.posterReady = false,
|
||||
this.fallback = PlayerOpeningFallback.black,
|
||||
this.warmSlotReused = false,
|
||||
this.waitForPlaybackFrame = false,
|
||||
});
|
||||
|
||||
@@ -34,6 +38,15 @@ class PlayerVideoSurface extends StatefulWidget {
|
||||
/// 当前集 t=0 雪碧图是否已命中缓存,仅用于调试判定链。
|
||||
final bool thumbnailReady;
|
||||
|
||||
/// 当前集高清 poster 是否已下发,仅用于调试判定链。
|
||||
final bool posterReady;
|
||||
|
||||
/// 首个视频帧确认前实际选择的唯一占位来源。
|
||||
final PlayerOpeningFallback fallback;
|
||||
|
||||
/// 当前集是否为相邻集预热槽晋升而来。
|
||||
final bool warmSlotReused;
|
||||
|
||||
/// 自动播放中的新集等待 position 首次前进后再移交画面。
|
||||
final bool waitForPlaybackFrame;
|
||||
final String videoUrl;
|
||||
@@ -90,6 +103,9 @@ class _PlayerVideoSurfaceState extends State<PlayerVideoSurface>
|
||||
_placeholderSnapshot = widget.placeholder;
|
||||
}
|
||||
if (oldWidget.thumbnailReady != widget.thumbnailReady ||
|
||||
oldWidget.posterReady != widget.posterReady ||
|
||||
oldWidget.fallback != widget.fallback ||
|
||||
oldWidget.warmSlotReused != widget.warmSlotReused ||
|
||||
oldWidget.warmFrameAvailable != widget.warmFrameAvailable) {
|
||||
final value = widget.controller?.value;
|
||||
_updatePlaybackFlags(value);
|
||||
@@ -285,14 +301,12 @@ class _PlayerVideoSurfaceState extends State<PlayerVideoSurface>
|
||||
if (!kDebugMode) return;
|
||||
final fallback = phase == 'video' || phase == 'fade-end'
|
||||
? 'video'
|
||||
: _placeholderSnapshot == null
|
||||
? 'black'
|
||||
: widget.thumbnailReady
|
||||
? 'thumbnails-first-frame'
|
||||
: 'black';
|
||||
: _fallbackLabel(widget.fallback);
|
||||
final signature =
|
||||
'phase=$phase warm=${widget.warmFrameAvailable ? 1 : 0} '
|
||||
'reusedWarm=${widget.warmSlotReused ? 1 : 0} '
|
||||
'frame=${_frameConfirmed ? 1 : 0} '
|
||||
'poster=${widget.posterReady ? 'ready' : 'none'} '
|
||||
'thumbnails=${widget.thumbnailReady ? 'ready' : 'pending'} '
|
||||
'fallback=$fallback '
|
||||
'init=${value?.isInitialized == true ? 1 : 0} '
|
||||
@@ -305,6 +319,16 @@ class _PlayerVideoSurfaceState extends State<PlayerVideoSurface>
|
||||
'[player-layer] ts=${DateTime.now().microsecondsSinceEpoch} $signature',
|
||||
);
|
||||
}
|
||||
|
||||
String _fallbackLabel(PlayerOpeningFallback fallback) {
|
||||
if (_placeholderSnapshot == null) return 'black';
|
||||
return switch (fallback) {
|
||||
PlayerOpeningFallback.handoff => 'handoff',
|
||||
PlayerOpeningFallback.poster => 'poster',
|
||||
PlayerOpeningFallback.thumbnails => 'thumbnails-first-frame',
|
||||
PlayerOpeningFallback.black => 'black',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// 将旧 controller 作为唯一占位,不额外叠加 handoff 层。
|
||||
|
||||
Reference in new issue
Block a user