From c7611e436f05226936853942265a4a04a158e2b1 Mon Sep 17 00:00:00 2001 From: orz12 Date: Sun, 24 Dec 2023 02:48:51 +0800 Subject: [PATCH] =?UTF-8?q?fix/opt:=20=E9=87=8D=E6=9E=84=E5=BC=B9=E5=B9=95?= =?UTF-8?q?=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 改用map存储,将同属于100毫秒内的弹幕归入一个元素,无需再排序和二分比较取得,降低时间复杂度与播放时功耗; 分离PlDanmakuController与playerController的功能,避免代码耦合; 精简用于表示状态的变量与相关逻辑,修复播放完毕后因currentSegIndex永久增加而无法再显示弹幕的错误; 为PlDanmakuController添加dispose()。 --- lib/pages/danmaku/controller.dart | 97 ++++++++++++----------------- lib/pages/danmaku/view.dart | 100 ++++++++++-------------------- 2 files changed, 72 insertions(+), 125 deletions(-) diff --git a/lib/pages/danmaku/controller.dart b/lib/pages/danmaku/controller.dart index 38d09e04..0d3f795f 100644 --- a/lib/pages/danmaku/controller.dart +++ b/lib/pages/danmaku/controller.dart @@ -3,74 +3,57 @@ import 'package:pilipala/models/danmaku/dm.pb.dart'; import 'package:pilipala/plugin/pl_player/index.dart'; class PlDanmakuController { - PlDanmakuController(this.cid, this.playerController); + PlDanmakuController(this.cid); final int cid; - final PlPlayerController playerController; late Duration videoDuration; - // 按 6min 分段 - int segCount = 0; - List dmSegList = []; + Map> dmSegMap = {}; // 已请求的段落标记 - List hasrequestSeg = []; - int currentSegIndex = 1; - int currentDmIndex = 0; + List requestedSeg = []; - void calcSegment() { - dmSegList.clear(); - // 视频分段数 - segCount = (videoDuration.inSeconds / (60 * 6)).ceil(); - dmSegList = List.generate( - segCount < 1 ? 1 : segCount, (index) => DmSegMobileReply()); - // 当前分段 - try { - currentSegIndex = - (playerController.position.value.inSeconds / (60 * 6)).ceil(); - currentSegIndex = currentSegIndex < 1 ? 1 : currentSegIndex; - } catch (_) {} + bool get initiated => requestedSeg.isNotEmpty; + + static int SEGMENT_LENGTH = 60 * 6 * 1000; + + void initiate(int progress) { + if (requestedSeg.isEmpty) { + int segCount = (videoDuration.inSeconds / (60 * 6)).ceil(); + requestedSeg = List.generate(segCount, (index) => false); + } + queryDanmaku( + calcSegment(progress) + ); } - Future> queryDanmaku() async { - // dmSegList.clear(); + void dispose() { + dmSegMap.clear(); + requestedSeg.clear(); + } + + int calcSegment(int progress) { + return progress ~/ SEGMENT_LENGTH; + } + + void queryDanmaku(int segmentIndex) async { + assert(requestedSeg[segmentIndex] == false); + requestedSeg[segmentIndex] = true; DmSegMobileReply result = - await DanmakaHttp.queryDanmaku(cid: cid, segmentIndex: currentSegIndex); + await DanmakaHttp.queryDanmaku(cid: cid, segmentIndex: segmentIndex + 1); if (result.elems.isNotEmpty) { - result.elems.sort((a, b) => (a.progress).compareTo(b.progress)); - // dmSegList.add(result); - currentSegIndex = currentSegIndex < 1 ? 1 : currentSegIndex; - dmSegList[currentSegIndex - 1] = result; + for (var element in result.elems) { + int pos = element.progress ~/ 100;//每0.1秒存储一次 + if (dmSegMap[pos] == null) { + dmSegMap[pos] = []; + } + dmSegMap[pos]!.add(element); + } } - if (dmSegList.isNotEmpty) { - findClosestPositionIndex(playerController.position.value.inMilliseconds); - } - return dmSegList; } - /// 查询当前最接近的弹幕 - void findClosestPositionIndex(int position) { - int segIndex = (position / (6 * 60 * 1000)).ceil() - 1; - if (segIndex < 0) segIndex = 0; - List elems = dmSegList[segIndex].elems; - - if (segIndex < dmSegList.length) { - int left = 0; - int right = elems.length; - - while (left < right) { - int mid = (right + left) ~/ 2; - var midPosition = elems[mid].progress; - - if (midPosition >= position) { - right = mid; - } else { - left = mid + 1; - } - } - - currentSegIndex = segIndex; - currentDmIndex = right; - } else { - currentSegIndex = segIndex; - currentDmIndex = 0; + List? getCurrentDanmaku(int progress) { + int segmentIndex = calcSegment(progress); + if (!requestedSeg[segmentIndex]) { + queryDanmaku(segmentIndex); } + return dmSegMap[progress ~/ 100]; } } diff --git a/lib/pages/danmaku/view.dart b/lib/pages/danmaku/view.dart index 1ff7677c..d7030ebf 100644 --- a/lib/pages/danmaku/view.dart +++ b/lib/pages/danmaku/view.dart @@ -3,6 +3,7 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:hive/hive.dart'; import 'package:ns_danmaku/ns_danmaku.dart'; +import 'package:pilipala/models/danmaku/dm.pb.dart'; import 'package:pilipala/pages/danmaku/index.dart'; import 'package:pilipala/plugin/pl_player/index.dart'; import 'package:pilipala/utils/danmaku.dart'; @@ -27,7 +28,7 @@ class _PlDanmakuState extends State { late PlPlayerController playerController; late PlDanmakuController _plDanmakuController; DanmakuController? _controller; - bool danmuPlayStatus = true; + // bool danmuPlayStatus = true; Box setting = GStrorage.setting; late bool enableShowDanmaku; late List blockTypes; @@ -35,6 +36,7 @@ class _PlDanmakuState extends State { late double opacityVal; late double fontSizeVal; late double danmakuDurationVal; + int latestAddedPosition = -1; @override void initState() { @@ -42,26 +44,24 @@ class _PlDanmakuState extends State { enableShowDanmaku = setting.get(SettingBoxKey.enableShowDanmaku, defaultValue: false); _plDanmakuController = - PlDanmakuController(widget.cid, widget.playerController); + PlDanmakuController(widget.cid); if (mounted) { playerController = widget.playerController; _plDanmakuController.videoDuration = playerController.duration.value; if (enableShowDanmaku || playerController.isOpenDanmu.value) { - _plDanmakuController - ..calcSegment() - ..queryDanmaku(); + _plDanmakuController.initiate( + playerController.position.value.inMilliseconds + ); } playerController ..addStatusLister(playerListener) ..addPositionListener(videoPositionListen); } playerController.isOpenDanmu.listen((p0) { - if (p0) { - if (_plDanmakuController.dmSegList.isEmpty) { - _plDanmakuController - ..calcSegment() - ..queryDanmaku(); - } + if (p0 && !_plDanmakuController.initiated) { + _plDanmakuController.initiate( + playerController.position.value.inMilliseconds + ); } }); blockTypes = playerController.blockTypes; @@ -82,68 +82,32 @@ class _PlDanmakuState extends State { } void videoPositionListen(Duration position) { - if (!danmuPlayStatus) { - _controller!.onResume(); - danmuPlayStatus = true; - } if (!playerController.isOpenDanmu.value) { return; } - PlDanmakuController ctr = _plDanmakuController; int currentPosition = position.inMilliseconds; - blockTypes = playerController.blockTypes; - // 根据position判断是否有已缓存弹幕。没有则请求对应段 - int segIndex = (currentPosition / (6 * 60 * 1000)).ceil(); - segIndex = segIndex < 1 ? 1 : segIndex; - // print('🌹🌹: ${segIndex}'); - // print('🌹🌹: ${ctr.dmSegList.length}'); - // print('🌹🌹: ${ctr.hasrequestSeg.contains(segIndex - 1)}'); - if (segIndex - 1 >= ctr.dmSegList.length || - (ctr.dmSegList[segIndex - 1].elems.isEmpty && - !ctr.hasrequestSeg.contains(segIndex - 1))) { - ctr.hasrequestSeg.add(segIndex - 1); - ctr.currentSegIndex = segIndex; - EasyThrottle.throttle('follow', const Duration(seconds: 1), () { - ctr.queryDanmaku(); - }); - } - // 超出分段数返回 - if (ctr.currentSegIndex >= ctr.dmSegList.length) { - return; - } - if (ctr.dmSegList.isEmpty || - ctr.dmSegList[ctr.currentSegIndex].elems.isEmpty) { - return; - } - // 超出当前分段的弹幕总数返回 - if (ctr.currentDmIndex >= ctr.dmSegList[ctr.currentSegIndex].elems.length) { - ctr.currentDmIndex = 0; - ctr.currentSegIndex++; - return; - } - var element = ctr.dmSegList[ctr.currentSegIndex].elems[ctr.currentDmIndex]; - var delta = currentPosition - element.progress; + currentPosition -= currentPosition % 100;//取整百的毫秒数 - if (delta >= 0 && delta < 200) { - // 屏蔽彩色弹幕 - if (blockTypes.contains(6) ? element.color == 16777215 : true) { - _controller!.addItems([ - DanmakuItem( - element.content, - color: DmUtils.decimalToColor(element.color), - time: element.progress, - type: DmUtils.getPosition(element.mode), - ) - ]); - } - ctr.currentDmIndex++; - } else { - if (!playerController.isOpenDanmu.value) { - _controller!.pause(); - danmuPlayStatus = false; - return; - } - ctr.findClosestPositionIndex(position.inMilliseconds); + if (currentPosition == latestAddedPosition) { + return; + } + latestAddedPosition = currentPosition; + + List? currentDanmakuList = + _plDanmakuController.getCurrentDanmaku(currentPosition); + + if (currentDanmakuList != null) { + Color? defaultColor = playerController.blockTypes.contains(6) ? + DmUtils.decimalToColor(16777215) : null; + + _controller!.addItems( + currentDanmakuList.map((e) => DanmakuItem( + e.content, + color: defaultColor ?? DmUtils.decimalToColor(e.color), + time: e.progress, + type: DmUtils.getPosition(e.mode), + )).toList() + ); } }