From e2708e77282507217ef3678155ffcf43050adeb6 Mon Sep 17 00:00:00 2001 From: bggRGjQaUbCoE Date: Mon, 4 Nov 2024 14:07:58 +0800 Subject: [PATCH] opt: live danmaku --- lib/pages/live_room/controller.dart | 3 + lib/pages/live_room/view.dart | 84 +++++++++++-- lib/pages/live_room/widgets/chat.dart | 118 ++++++++++-------- .../video/detail/reply_reply/controller.dart | 9 +- lib/pages/video/detail/view.dart | 12 +- lib/plugin/pl_player/controller.dart | 2 +- lib/plugin/pl_player/view.dart | 2 +- lib/tcp/live.dart | 6 +- 8 files changed, 156 insertions(+), 80 deletions(-) diff --git a/lib/pages/live_room/controller.dart b/lib/pages/live_room/controller.dart index b4fe627d..2f0f39d6 100644 --- a/lib/pages/live_room/controller.dart +++ b/lib/pages/live_room/controller.dart @@ -3,6 +3,7 @@ import 'package:PiliPalaX/http/constants.dart'; import 'package:PiliPalaX/http/live.dart'; import 'package:PiliPalaX/models/live/room_info.dart'; import 'package:PiliPalaX/plugin/pl_player/index.dart'; +import 'package:ns_danmaku/danmaku_controller.dart'; import '../../models/live/room_info_h5.dart'; import '../../utils/video_utils.dart'; @@ -21,6 +22,8 @@ class LiveRoomController extends GetxController { RxList messages = [].obs; RxBool disableAutoScroll = false.obs; + double? brightness; + DanmakuController? controller; @override void onInit() { diff --git a/lib/pages/live_room/view.dart b/lib/pages/live_room/view.dart index 554d8ee2..c2bbacd9 100644 --- a/lib/pages/live_room/view.dart +++ b/lib/pages/live_room/view.dart @@ -9,6 +9,10 @@ import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; import 'package:PiliPalaX/common/widgets/network_img_layer.dart'; import 'package:PiliPalaX/plugin/pl_player/index.dart'; +import 'package:ns_danmaku/danmaku_controller.dart'; +import 'package:ns_danmaku/danmaku_view.dart'; +import 'package:ns_danmaku/models/danmaku_option.dart'; +import 'package:screen_brightness/screen_brightness.dart'; import '../../utils/storage.dart'; import 'controller.dart'; @@ -24,7 +28,7 @@ class LiveRoomPage extends StatefulWidget { class _LiveRoomPageState extends State { late final int _roomId; final LiveRoomController _liveRoomController = Get.put(LiveRoomController()); - PlPlayerController? plPlayerController; + late final PlPlayerController plPlayerController; late Future? _futureBuilder; late Future? _futureBuilderFuture; @@ -36,8 +40,18 @@ class _LiveRoomPageState extends State { late final _node = FocusNode(); late final _ctr = TextEditingController(); + late bool enableShowDanmaku; + late List blockTypes; + late double showArea; + late double opacityVal; + late double fontSizeVal; + late double danmakuDurationVal; + late double strokeWidth; + late int fontWeight; + int latestAddedPosition = -1; + void playCallBack() { - plPlayerController?.play(); + plPlayerController.play(); } @override @@ -50,20 +64,28 @@ class _LiveRoomPageState extends State { } videoSourceInit(); _futureBuilderFuture = _liveRoomController.queryLiveInfo(); - plPlayerController!.autoEnterFullscreen(); + plPlayerController.autoEnterFullscreen(); } Future videoSourceInit() async { _futureBuilder = _liveRoomController.queryLiveInfoH5(); plPlayerController = _liveRoomController.plPlayerController; + blockTypes = plPlayerController.blockTypes; + showArea = plPlayerController.showArea; + opacityVal = plPlayerController.opacityVal; + fontSizeVal = plPlayerController.fontSizeVal; + strokeWidth = plPlayerController.strokeWidth; + fontWeight = plPlayerController.fontWeight; + danmakuDurationVal = plPlayerController.danmakuDurationVal; } @override void dispose() { + ScreenBrightness().resetApplicationScreenBrightness(); PlPlayerController.setPlayCallBack(null); floating?.dispose(); _node.dispose(); - plPlayerController?.dispose(); + plPlayerController.dispose(); _ctr.dispose(); super.dispose(); } @@ -75,12 +97,39 @@ class _LiveRoomPageState extends State { builder: (BuildContext context, AsyncSnapshot snapshot) { if (snapshot.hasData && snapshot.data['status']) { return PLVideoPlayer( - controller: plPlayerController!, + controller: plPlayerController, bottomControl: BottomControl( controller: plPlayerController, liveRoomCtr: _liveRoomController, floating: floating, ), + danmuWidget: Obx( + () => AnimatedOpacity( + opacity: plPlayerController.isOpenDanmu.value ? 1 : 0, + duration: const Duration(milliseconds: 100), + child: DanmakuView( + createdController: (DanmakuController e) async { + plPlayerController.danmakuController = + _liveRoomController.controller = e; + }, + option: DanmakuOption( + fontSize: 15 * fontSizeVal, + fontWeight: fontWeight, + area: showArea, + opacity: opacityVal, + hideTop: blockTypes.contains(5), + hideScroll: blockTypes.contains(2), + hideBottom: blockTypes.contains(4), + duration: + danmakuDurationVal / plPlayerController.playbackSpeed, + strokeWidth: strokeWidth, + // initDuration / + // (danmakuSpeedVal * widget.playerController.playbackSpeed), + ), + statusChanged: (isPlaying) {}, + ), + ), + ), ); } else { return const SizedBox(); @@ -236,10 +285,10 @@ class _LiveRoomPageState extends State { ), ), PopScope( - canPop: plPlayerController?.isFullScreen.value != true, + canPop: plPlayerController.isFullScreen.value != true, onPopInvokedWithResult: (bool didPop, Object? result) { - if (plPlayerController?.isFullScreen.value == true) { - plPlayerController!.triggerFullScreen(status: false); + if (plPlayerController.isFullScreen.value == true) { + plPlayerController.triggerFullScreen(status: false); } if (MediaQuery.of(context).orientation == Orientation.landscape) { @@ -273,9 +322,9 @@ class _LiveRoomPageState extends State { ), Container( padding: EdgeInsets.only( - left: 16, + left: 10, top: 10, - right: 16, + right: 10, bottom: 25 + MediaQuery.of(context).padding.bottom, ), decoration: BoxDecoration( @@ -290,6 +339,21 @@ class _LiveRoomPageState extends State { ), child: Row( children: [ + Obx( + () => IconButton( + onPressed: () { + plPlayerController.isOpenDanmu.value = + !plPlayerController.isOpenDanmu.value; + GStorage.setting.put(SettingBoxKey.enableShowDanmaku, + plPlayerController.isOpenDanmu.value); + }, + icon: Icon( + plPlayerController.isOpenDanmu.value + ? Icons.subtitles_outlined + : Icons.subtitles_off_outlined, + ), + ), + ), Expanded( child: TextField( focusNode: _node, diff --git a/lib/pages/live_room/widgets/chat.dart b/lib/pages/live_room/widgets/chat.dart index 671e7137..4124c64b 100644 --- a/lib/pages/live_room/widgets/chat.dart +++ b/lib/pages/live_room/widgets/chat.dart @@ -6,12 +6,13 @@ import 'package:PiliPalaX/models/live/danmu_info.dart'; import 'package:PiliPalaX/pages/live_room/controller.dart'; import 'package:PiliPalaX/services/loggeer.dart'; import 'package:PiliPalaX/tcp/live.dart'; +import 'package:PiliPalaX/utils/danmaku.dart'; import 'package:PiliPalaX/utils/utils.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; -import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; +import 'package:ns_danmaku/models/danmaku_item.dart'; import '../../../utils/storage.dart'; @@ -50,56 +51,58 @@ class _LiveRoomChatState extends State { Widget build(BuildContext context) { return Stack( children: [ - ListView.separated( - controller: _scrollController, - separatorBuilder: (_, index) => const SizedBox(height: 6), - itemCount: widget.liveRoomController.messages.length, - itemBuilder: (context, index) { - return Container( - alignment: Alignment.centerLeft, - padding: const EdgeInsets.symmetric(horizontal: 16), - child: Container( - padding: - const EdgeInsets.symmetric(horizontal: 10, vertical: 5), - decoration: const BoxDecoration( - color: Color(0x15FFFFFF), - borderRadius: BorderRadius.all(Radius.circular(18)), - ), - child: Text.rich( - TextSpan( - children: [ - TextSpan( - text: - '${widget.liveRoomController.messages[index]['info'][0][15]['user']['base']['name']}: ', - style: const TextStyle( - color: Color(0xFFAAAAAA), - fontSize: 14, + Obx( + () => ListView.separated( + controller: _scrollController, + separatorBuilder: (_, index) => const SizedBox(height: 6), + itemCount: widget.liveRoomController.messages.length, + itemBuilder: (context, index) { + return Container( + alignment: Alignment.centerLeft, + padding: const EdgeInsets.symmetric(horizontal: 16), + child: Container( + padding: + const EdgeInsets.symmetric(horizontal: 10, vertical: 5), + decoration: const BoxDecoration( + color: Color(0x15FFFFFF), + borderRadius: BorderRadius.all(Radius.circular(18)), + ), + child: Text.rich( + TextSpan( + children: [ + TextSpan( + text: + '${widget.liveRoomController.messages[index]['info'][0][15]['user']['base']['name']}: ', + style: const TextStyle( + color: Color(0xFFAAAAAA), + fontSize: 14, + ), + recognizer: TapGestureRecognizer() + ..onTap = () { + try { + dynamic uid = + widget.liveRoomController.messages[index] + ['info'][0][15]['user']['uid']; + Get.toNamed( + '/member?mid=$uid', + arguments: { + 'heroTag': Utils.makeHeroTag(uid), + }, + ); + } catch (err) { + print(err.toString()); + // SmartDialog.showToast(err.toString()); + } + }, ), - recognizer: TapGestureRecognizer() - ..onTap = () { - try { - dynamic uid = - widget.liveRoomController.messages[index] - ['info'][0][15]['user']['uid']; - Get.toNamed( - '/member?mid=$uid', - arguments: { - 'heroTag': Utils.makeHeroTag(uid), - }, - ); - } catch (err) { - print(err.toString()); - // SmartDialog.showToast(err.toString()); - } - }, - ), - _buildMsg(widget.liveRoomController.messages[index]), - ], + _buildMsg(widget.liveRoomController.messages[index]), + ], + ), ), ), - ), - ); - }, + ); + }, + ), ), Obx( () => widget.liveRoomController.disableAutoScroll.value @@ -138,12 +141,19 @@ class _LiveRoomChatState extends State { msgStream?.addEventListener((obj) { if (obj['cmd'] == 'DANMU_MSG') { // logger.i(' 原始弹幕消息 ======> ${jsonEncode(obj)}'); - setState(() { - widget.liveRoomController.messages.add(obj); - WidgetsBinding.instance.addPostFrameCallback( - (_) => _scrollToBottom(), - ); - }); + widget.liveRoomController.messages.add(obj); + Map json = jsonDecode(obj['info'][0][15]['extra']); + widget.liveRoomController.controller?.addItems([ + DanmakuItem( + json['content'], + color: DmUtils.decimalToColor(json['color']), + // time: e.progress, + type: DmUtils.getPosition(json['mode']), + ) + ]); + WidgetsBinding.instance.addPostFrameCallback( + (_) => _scrollToBottom(), + ); } }); msgStream?.init(); diff --git a/lib/pages/video/detail/reply_reply/controller.dart b/lib/pages/video/detail/reply_reply/controller.dart index 831090b8..51b82b0b 100644 --- a/lib/pages/video/detail/reply_reply/controller.dart +++ b/lib/pages/video/detail/reply_reply/controller.dart @@ -115,16 +115,15 @@ class VideoReplyReplyController extends CommonController begin: Theme.of(Get.context!).colorScheme.onInverseSurface, end: Theme.of(Get.context!).colorScheme.surface, ).animate(controller!); - () async { - await Future.delayed(const Duration(milliseconds: 200)); + WidgetsBinding.instance.addPostFrameCallback((_) async { itemScrollCtr.jumpTo( - index: hasRoot ? index! + 3 : index! + 1, alignment: 0.25, - // duration: const Duration(milliseconds: 200), + index: hasRoot ? index! + 3 : index! + 1, + alignment: 0.25, ); await Future.delayed(const Duration(milliseconds: 800)); await controller?.forward(); index = null; - }(); + }); } id = null; } diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index 280d8f24..e81d1cee 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -276,7 +276,7 @@ class _VideoDetailPageState extends State @override void dispose() { if (!Get.previousRoute.startsWith('/video')) { - ScreenBrightness().resetScreenBrightness(); + ScreenBrightness().resetApplicationScreenBrightness(); PlPlayerController.setPlayCallBack(null); } videoDetailController.positionSubscription?.cancel(); @@ -310,7 +310,7 @@ class _VideoDetailPageState extends State void didPushNext() async { // _bufferedListener?.cancel(); - ScreenBrightness().resetScreenBrightness(); + ScreenBrightness().resetApplicationScreenBrightness(); videoDetailController.positionSubscription?.cancel(); videoIntroController.canelTimer(); @@ -344,13 +344,13 @@ class _VideoDetailPageState extends State plPlayerController ?.setCurrBrightness(videoDetailController.brightness!); if (videoDetailController.brightness != -1.0) { - ScreenBrightness() - .setScreenBrightness(videoDetailController.brightness!); + ScreenBrightness().setApplicationScreenBrightness( + videoDetailController.brightness!); } else { - ScreenBrightness().resetScreenBrightness(); + ScreenBrightness().resetApplicationScreenBrightness(); } } else { - ScreenBrightness().resetScreenBrightness(); + ScreenBrightness().resetApplicationScreenBrightness(); } } super.didPopNext(); diff --git a/lib/plugin/pl_player/controller.dart b/lib/plugin/pl_player/controller.dart index 5462ac33..42191c0a 100644 --- a/lib/plugin/pl_player/controller.dart +++ b/lib/plugin/pl_player/controller.dart @@ -1035,7 +1035,7 @@ class PlPlayerController { Future setBrightness(double brightness) async { try { this.brightness.value = brightness; - ScreenBrightness().setScreenBrightness(brightness); + ScreenBrightness().setApplicationScreenBrightness(brightness); // setVideoBrightness(); } catch (e) { throw 'Failed to set brightness'; diff --git a/lib/plugin/pl_player/view.dart b/lib/plugin/pl_player/view.dart index 1f26e15f..baa6b96e 100644 --- a/lib/plugin/pl_player/view.dart +++ b/lib/plugin/pl_player/view.dart @@ -205,7 +205,7 @@ class _PLVideoPlayerState extends State Future setBrightness(double value) async { try { - await ScreenBrightness().setScreenBrightness(value); + await ScreenBrightness().setApplicationScreenBrightness(value); } catch (_) {} _brightnessIndicator.value = true; _brightnessTimer?.cancel(); diff --git a/lib/tcp/live.dart b/lib/tcp/live.dart index 71a58693..454d59eb 100644 --- a/lib/tcp/live.dart +++ b/lib/tcp/live.dart @@ -216,13 +216,13 @@ class LiveMessageStream { } _processingData(decompressedData); } catch (e) { - logger.w(e); + logger.i(e); } } } socket.close(); } catch (e) { - logger.e('$logTag ===> TCP连接失败: $e'); + logger.i('$logTag ===> TCP连接失败: $e'); } } @@ -241,7 +241,7 @@ class LiveMessageStream { } } } catch (e) { - logger.e('ParseHeader错误: $e'); + logger.i('ParseHeader错误: $e'); } }