From 75285262524e07c10e22c905eddaf0030d8b30e5 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Thu, 10 Aug 2023 11:50:45 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E8=A7=86=E9=A2=91=E8=A7=A3=E7=A0=81?= =?UTF-8?q?=E6=A0=BC=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/models/video/play/quality.dart | 35 +++++ lib/pages/video/detail/controller.dart | 17 ++- .../video/detail/widgets/header_control.dart | 122 +++++++++++++++--- 3 files changed, 150 insertions(+), 24 deletions(-) diff --git a/lib/models/video/play/quality.dart b/lib/models/video/play/quality.dart index 7536b971..80313b6d 100644 --- a/lib/models/video/play/quality.dart +++ b/lib/models/video/play/quality.dart @@ -89,3 +89,38 @@ extension AudioQualityDesc on AudioQuality { ]; get description => _descList[index]; } + +enum VideoDecodeFormats { + AV1, + HEVC, + AVC, +} + +extension VideoDecodeFormatsDesc on VideoDecodeFormats { + static final List _descList = [ + 'AV1', + 'HEVC', + 'AVC', + ]; + get description => _descList[index]; +} + +extension VideoDecodeFormatsCode on VideoDecodeFormats { + static final List _codeList = [ + 'av01', + 'hev1', + 'avc1', + ]; + get code => _codeList[index]; + + static VideoDecodeFormats? fromString(String val) { + var result = VideoDecodeFormats.values.first; + for (var i in _codeList) { + if (val.startsWith(i)) { + result = VideoDecodeFormats.values[_codeList.indexOf(i)]; + break; + } + } + return result; + } +} diff --git a/lib/pages/video/detail/controller.dart b/lib/pages/video/detail/controller.dart index adbedcba..9af63aa8 100644 --- a/lib/pages/video/detail/controller.dart +++ b/lib/pages/video/detail/controller.dart @@ -32,6 +32,8 @@ class VideoDetailController extends GetxController late VideoQuality currentVideoQa; // 当前音质 late AudioQuality currentAudioQa; + // 当前解码格式 + late VideoDecodeFormats currentDecodeFormats; // 是否预渲染 骨架屏 bool preRender = false; @@ -114,9 +116,11 @@ class VideoDetailController extends GetxController /// 暂不匹配解码规则 - /// 根据currentVideoQa 重新设置videoUrl - VideoItem firstVideo = - data.dash!.video!.firstWhere((i) => i.id == currentVideoQa.code); + /// 根据currentVideoQa和currentDecodeFormats 重新设置videoUrl + List videoList = + data.dash!.video!.where((i) => i.id == currentVideoQa.code).toList(); + VideoItem firstVideo = videoList + .firstWhere((i) => i.codecs!.startsWith(currentDecodeFormats.code)); // String videoUrl = firstVideo.baseUrl!; /// 根据currentAudioQa 重新设置audioUrl @@ -176,6 +180,13 @@ class VideoDetailController extends GetxController if (firstAudio.id != null) { currentAudioQa = AudioQualityCode.fromCode(firstAudio.id!)!; } + + /// 优先顺序 设置中指定解码格式 -> 当前可选的首个解码格式 + List supportFormats = data.supportFormats!; + List supportDecodeFormats = supportFormats.first.codecs!; + currentDecodeFormats = + VideoDecodeFormatsCode.fromString(supportDecodeFormats.first)!; + await playerInit( firstVideo, audioUrl, diff --git a/lib/pages/video/detail/widgets/header_control.dart b/lib/pages/video/detail/widgets/header_control.dart index bedcd008..9a429d11 100644 --- a/lib/pages/video/detail/widgets/header_control.dart +++ b/lib/pages/video/detail/widgets/header_control.dart @@ -28,6 +28,7 @@ class _HeaderControlState extends State { late PlayUrlModel videoInfo; List playSpeed = PlaySpeed.values; TextStyle subTitleStyle = const TextStyle(fontSize: 12); + TextStyle titleStyle = const TextStyle(fontSize: 14); Size get preferredSize => const Size(double.infinity, kToolbarHeight); @override @@ -81,7 +82,7 @@ class _HeaderControlState extends State { enabled: false, leading: const Icon(Icons.network_cell_outlined, size: 20), - title: const Text('省流模式'), + title: Text('省流模式', style: titleStyle), subtitle: Text('低画质 | 减少视频缓存', style: subTitleStyle), trailing: Transform.scale( scale: 0.75, @@ -99,22 +100,22 @@ class _HeaderControlState extends State { ), ), ), - Obx( - () => ListTile( - onTap: () => {Get.back(), showSetSpeedSheet()}, - dense: true, - leading: const Icon(Icons.speed_outlined, size: 20), - title: const Text('播放速度'), - subtitle: Text( - '当前倍速 x${widget.controller!.playbackSpeed}', - style: subTitleStyle), - ), - ), + // Obx( + // () => ListTile( + // onTap: () => {Get.back(), showSetSpeedSheet()}, + // dense: true, + // leading: const Icon(Icons.speed_outlined, size: 20), + // title: Text('播放速度', style: titleStyle), + // subtitle: Text( + // '当前倍速 x${widget.controller!.playbackSpeed}', + // style: subTitleStyle), + // ), + // ), ListTile( onTap: () => {Get.back(), showSetVideoQa()}, dense: true, leading: const Icon(Icons.play_circle_outline, size: 20), - title: const Text('选择画质'), + title: Text('选择画质', style: titleStyle), subtitle: Text( '当前画质 ${widget.videoDetailCtr!.currentVideoQa.description}', style: subTitleStyle), @@ -123,24 +124,33 @@ class _HeaderControlState extends State { onTap: () => {Get.back(), showSetAudioQa()}, dense: true, leading: const Icon(Icons.album_outlined, size: 20), - title: const Text('选择音质'), + title: Text('选择音质', style: titleStyle), subtitle: Text( '当前音质 ${widget.videoDetailCtr!.currentAudioQa.description}', style: subTitleStyle), ), ListTile( - onTap: () {}, + onTap: () => {Get.back(), showSetDecodeFormats()}, dense: true, - enabled: false, - leading: const Icon(Icons.play_circle_outline, size: 20), - title: const Text('播放设置'), + leading: const Icon(Icons.av_timer_outlined, size: 20), + title: Text('解码格式', style: titleStyle), + subtitle: Text( + '当前解码格式 ${widget.videoDetailCtr!.currentDecodeFormats.description}', + style: subTitleStyle), ), + // ListTile( + // onTap: () {}, + // dense: true, + // enabled: false, + // leading: const Icon(Icons.play_circle_outline, size: 20), + // title: Text('播放设置', style: titleStyle), + // ), ListTile( onTap: () {}, dense: true, enabled: false, leading: const Icon(Icons.subtitles_outlined, size: 20), - title: const Text('弹幕设置'), + title: Text('弹幕设置', style: titleStyle), ), ], ), @@ -250,7 +260,7 @@ class _HeaderControlState extends State { child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ - const Text('选择画质'), + Text('选择画质', style: titleStyle), const SizedBox(width: 4), Icon( Icons.info_outline, @@ -329,7 +339,9 @@ class _HeaderControlState extends State { margin: const EdgeInsets.all(12), child: Column( children: [ - const SizedBox(height: 45, child: Center(child: Text('选择音质'))), + SizedBox( + height: 45, + child: Center(child: Text('选择音质', style: titleStyle))), Expanded( child: Material( child: ListView( @@ -370,6 +382,74 @@ class _HeaderControlState extends State { ); } + // 选择解码格式 + void showSetDecodeFormats() { + // 当前选中的解码格式 + VideoDecodeFormats currentDecodeFormats = + widget.videoDetailCtr!.currentDecodeFormats; + // 当前视频可用的解码格式 + List videoFormat = videoInfo.supportFormats!; + List list = videoFormat.first.codecs!; + + showModalBottomSheet( + context: context, + elevation: 0, + backgroundColor: Colors.transparent, + builder: (BuildContext context) { + return Container( + width: double.infinity, + height: 250, + clipBehavior: Clip.hardEdge, + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.background, + borderRadius: const BorderRadius.all(Radius.circular(12)), + ), + margin: const EdgeInsets.all(12), + child: Column( + children: [ + SizedBox( + height: 45, + child: Center(child: Text('选择解码格式', style: titleStyle))), + Expanded( + child: Material( + child: ListView( + children: [ + for (var i in list) ...[ + ListTile( + onTap: () { + widget.videoDetailCtr!.currentDecodeFormats = + VideoDecodeFormatsCode.fromString(i)!; + widget.videoDetailCtr!.updatePlayer(); + Get.back(); + }, + dense: true, + contentPadding: + const EdgeInsets.only(left: 20, right: 20), + title: Text(VideoDecodeFormatsCode.fromString(i)! + .description!), + subtitle: Text( + i!, + style: subTitleStyle, + ), + trailing: i.startsWith(currentDecodeFormats.code) + ? Icon( + Icons.done, + color: Theme.of(context).colorScheme.primary, + ) + : const SizedBox(), + ), + ] + ], + ), + ), + ), + ], + ), + ); + }, + ); + } + @override Widget build(BuildContext context) { final _ = widget.controller!;