2.7 KiB
2.7 KiB
播放器进度拖动画面预览
目标
首页 feed 与全屏播放器的进度条在拖动时显示当前时间点的视频画面缩略图、 当前时间与总时长;松手立即隐藏并提交 seek。缩略图未返回或失败时显示短剧 封面,不出现空白闪烁或错误文案。
性能红线
- 拖动期间主线程无卡顿;图片解码和原生取帧不得阻塞 UI 线程。
- 原生取帧并发数不超过 1。
- 预览图最大宽度 240px。
- Dart 内存 LRU 缓存不超过 24 帧。
- 不新增 pub 依赖。
- 不改后端契约。
实现
缩略图服务
- 新增
PlaybackThumbnailService,公开入口按episodeId + position取ByteData,底层通过MethodChannel('com.stormg.lumadrama/thumbnail')调用原生。 - 时间按 1 秒分桶;同一 bucket 的在途请求合流。
- 最多 24 帧 LRU;成功和失败结果都做过期控制,避免旧拖动位置覆盖当前预览。
- 全局单飞;拖动期间只保留最新 bucket,尚未开始的旧请求直接丢弃。
- 拖动停顿 120ms 后最多预取相邻一个 bucket,松手取消尚未开始的请求。
- 原生异常、缺失实现和超过 1.5 秒无结果统一返回
null。
原生取帧
- Android 使用
MediaMetadataRetriever,优先本地下载路径,其次签名 URL;OPTION_CLOSEST_SYNC取帧,最大宽 240px,JPEG 质量 70,单线程 Executor, 完成后立即 release。 - iOS 使用
AVAssetImageGenerator,应用画面方向,最大尺寸约 240x426, 合理时间容差,后台串行队列生成 JPEG。 - 两端均保证只回调一次;失败或 1.5 秒超时返回
null,不向 UI 抛异常。
UI
- 新增
ScrubPreviewCard:宽 120dp、圆角 8、白色 1px 描边,显示当前时间 / 总时长;沿拖动点水平跟随并做安全边距 clamp。 - 首页
_FeedProgressBar与全屏SeekProgressBar共用该卡片。 - 封面始终铺在卡片底层;
Image.memory仅在帧到达后覆盖,并限制cacheWidth: 240。 - 开始拖动请求当前 bucket,拖动更新只保留最新位置;松手立即隐藏卡片并取消 未开始请求。
PlayerPage持有页面级服务,dispose 时清空缓存,避免跨页面串集。
测试
- 单测:bucket、LRU 淘汰、同 bucket 合流、过期请求丢弃、松手取消、 相邻 bucket 防抖、异常/超时回落。
- Widget:拖动显示预览卡片和时间、松手消失、无缩略图显示封面。
- 门禁:
dart analyze lib test与flutter test --no-pub。
非目标
- 不改服务端接口或持久化策略。
- 不新增第三方依赖。
- 不启停服务、不操作真机;原生取帧实际效果由 PM 真机验收。