From 95c35cac58a45eba5ab1550d31dd72ef0c814e14 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 12 Jan 2025 18:01:34 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E7=9B=B4=E6=92=AD=E7=94=BB=E8=B4=A8?= =?UTF-8?q?=E5=88=87=E6=8D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes #146 Co-authored-by: bggRGjQaUbCoE --- lib/models/live/quality.dart | 43 +++++++++++++++++++ lib/pages/live_room/controller.dart | 33 +++++++++++++- .../live_room/widgets/bottom_control.dart | 40 ++++++++++++++--- 3 files changed, 108 insertions(+), 8 deletions(-) create mode 100644 lib/models/live/quality.dart diff --git a/lib/models/live/quality.dart b/lib/models/live/quality.dart new file mode 100644 index 00000000..47938416 --- /dev/null +++ b/lib/models/live/quality.dart @@ -0,0 +1,43 @@ +enum LiveQuality { + dolby, + super4K, + origin, + veryHigh, + bluRay, + superHD, + smooth, + flunt, +} + +extension LiveQualityCode on LiveQuality { + static final List _codeList = [ + 30000, + 20000, + 10000, + 400, + 250, + 150, + 80, + ]; + int get code => _codeList[index]; + + static LiveQuality? fromCode(int code) { + final index = _codeList.indexOf(code); + if (index != -1) { + return LiveQuality.values[index]; + } + return null; + } +} + +extension VideoQualityDesc on LiveQuality { + static final List _descList = [ + '杜比', + '4K', + '原画', + '蓝光', + '超清', + '流畅', + ]; + get description => _descList[index]; +} diff --git a/lib/pages/live_room/controller.dart b/lib/pages/live_room/controller.dart index 18243930..bd9f7cf7 100644 --- a/lib/pages/live_room/controller.dart +++ b/lib/pages/live_room/controller.dart @@ -1,6 +1,7 @@ import 'dart:convert'; import 'package:PiliPlus/models/live/danmu_info.dart'; +import 'package:PiliPlus/models/live/quality.dart'; import 'package:PiliPlus/tcp/live.dart'; import 'package:PiliPlus/utils/danmaku.dart'; import 'package:PiliPlus/utils/storage.dart'; @@ -31,6 +32,10 @@ class LiveRoomController extends GetxController { DanmakuController? controller; bool showDanmaku = true; + late int currentQn = 10000; + late List acceptQnList = []; + RxString currentQnDesc = ''.obs; + @override void onInit() { super.onInit(); @@ -58,12 +63,26 @@ class LiveRoomController extends GetxController { bool? isPortrait; Future queryLiveInfo() async { - var res = await LiveHttp.liveRoomInfo(roomId: roomId, qn: 10000); + var res = await LiveHttp.liveRoomInfo(roomId: roomId, qn: currentQn); if (res['status']) { isPortrait = res['data'].isPortrait; List codec = res['data'].playurlInfo.playurl.stream.first.format.first.codec; CodecItem item = codec.first; + // 以服务端返回的码率为准 + currentQn = item.currentQn!; + List acceptQn = item.acceptQn!; + acceptQnList = acceptQn.map((e) { + return { + 'code': e, + 'desc': LiveQuality.values + .firstWhere((element) => element.code == e) + .description, + }; + }).toList(); + currentQnDesc.value = LiveQuality.values + .firstWhere((element) => element.code == currentQn) + .description; String videoUrl = VideoUtils.getCdnUrl(item); await playerInit(videoUrl); return res; @@ -183,4 +202,16 @@ class LiveRoomController extends GetxController { scrollController.dispose(); super.onClose(); } + + // 修改画质 + void changeQn(int qn) async { + if (currentQn == qn) { + return; + } + currentQn = qn; + currentQnDesc.value = LiveQuality.values + .firstWhere((element) => element.code == currentQn) + .description; + await queryLiveInfo(); + } } diff --git a/lib/pages/live_room/widgets/bottom_control.dart b/lib/pages/live_room/widgets/bottom_control.dart index 21de0c42..a92cfb00 100644 --- a/lib/pages/live_room/widgets/bottom_control.dart +++ b/lib/pages/live_room/widgets/bottom_control.dart @@ -5,14 +5,15 @@ import 'package:flutter/material.dart'; import 'package:PiliPlus/models/video/play/url.dart'; import 'package:PiliPlus/pages/live_room/index.dart'; import 'package:PiliPlus/plugin/pl_player/index.dart'; +import 'package:get/get.dart'; class BottomControl extends StatefulWidget implements PreferredSizeWidget { - final PlPlayerController? controller; - final LiveRoomController? liveRoomCtr; + final PlPlayerController controller; + final LiveRoomController liveRoomCtr; final Floating? floating; const BottomControl({ - this.controller, - this.liveRoomCtr, + required this.controller, + required this.liveRoomCtr, this.floating, super.key, }); @@ -87,7 +88,7 @@ class _BottomControlState extends State { ), onPressed: () async { bool canUsePiP = false; - widget.controller!.hiddenControls(false); + widget.controller.hiddenControls(false); try { canUsePiP = await widget.floating!.isPipAvailable; } catch (_) {} @@ -104,6 +105,31 @@ class _BottomControlState extends State { ), const SizedBox(width: 4), ], + SizedBox( + width: 30, + child: PopupMenuButton( + padding: EdgeInsets.zero, + initialValue: widget.liveRoomCtr.currentQn, + onSelected: (value) { + widget.liveRoomCtr.changeQn(value); + }, + child: Obx( + () => Text( + widget.liveRoomCtr.currentQnDesc.value, + style: const TextStyle(color: Colors.white, fontSize: 13), + ), + ), + itemBuilder: (BuildContext context) { + return widget.liveRoomCtr.acceptQnList.map((e) { + return PopupMenuItem( + value: e['code'], + child: Text(e['desc']), + ); + }).toList(); + }, + ), + ), + const SizedBox(width: 10), ComBtn( icon: const Icon( Icons.fullscreen, @@ -111,8 +137,8 @@ class _BottomControlState extends State { size: 20, color: Colors.white, ), - fuc: () => widget.controller!.triggerFullScreen( - status: !widget.controller!.isFullScreen.value), + fuc: () => widget.controller.triggerFullScreen( + status: !widget.controller.isFullScreen.value), ), ], ),