diff --git a/lib/common/widgets/image/cached_network_svg_image.dart b/lib/common/widgets/image/cached_network_svg_image.dart index f1d648e47..fb2009ad0 100644 --- a/lib/common/widgets/image/cached_network_svg_image.dart +++ b/lib/common/widgets/image/cached_network_svg_image.dart @@ -60,9 +60,7 @@ class _CachedNetworkSVGImageState extends State { double? height; late TextScaler textScaler; - static final _sizeRegExp = RegExp( - r'height="([\d\.]+)([c-x]{2})?"', - ); + static final _sizeRegExp = RegExp(r'height="([\d\.]+)([c-x]{2})?"'); @override void initState() { diff --git a/lib/pages/setting/models/extra_settings.dart b/lib/pages/setting/models/extra_settings.dart index edaf98373..de75269d4 100644 --- a/lib/pages/setting/models/extra_settings.dart +++ b/lib/pages/setting/models/extra_settings.dart @@ -16,6 +16,7 @@ import 'package:PiliPlus/pages/setting/widgets/slider_dialog.dart'; import 'package:PiliPlus/services/download/download_service.dart'; import 'package:PiliPlus/utils/accounts.dart'; import 'package:PiliPlus/utils/extension/num_ext.dart'; +import 'package:PiliPlus/utils/filtering_text.dart'; import 'package:PiliPlus/utils/path_utils.dart'; import 'package:PiliPlus/utils/platform_utils.dart'; import 'package:PiliPlus/utils/storage.dart'; @@ -24,7 +25,6 @@ import 'package:PiliPlus/utils/storage_pref.dart'; import 'package:PiliPlus/utils/utils.dart'; import 'package:file_picker/file_picker.dart'; import 'package:flutter/material.dart' hide RefreshIndicator; -import 'package:flutter/services.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; import 'package:material_design_icons_flutter/material_design_icons_flutter.dart'; @@ -229,9 +229,7 @@ void _showTouchSlopDialog(BuildContext context, VoidCallback setState) { initialValue: initialValue, keyboardType: const .numberWithOptions(decimal: true), onChanged: (value) => initialValue = value, - inputFormatters: [ - FilteringTextInputFormatter.allow(RegExp(r'[\d\.]+')), - ], + inputFormatters: FilteringText.decimal, ), actions: [ TextButton( @@ -417,9 +415,7 @@ void _showCacheDialog(BuildContext context, VoidCallback setState) { autofocus: true, onChanged: (value) => valueStr = value, keyboardType: TextInputType.number, - inputFormatters: [ - FilteringTextInputFormatter.allow(RegExp(r'[\d\.]+')), - ], + inputFormatters: FilteringText.decimal, decoration: const InputDecoration(suffixText: 'MB'), ), actions: [ diff --git a/lib/pages/setting/models/video_settings.dart b/lib/pages/setting/models/video_settings.dart index f3498eeab..c1fc88fcf 100644 --- a/lib/pages/setting/models/video_settings.dart +++ b/lib/pages/setting/models/video_settings.dart @@ -10,12 +10,14 @@ import 'package:PiliPlus/pages/setting/widgets/ordered_multi_select_dialog.dart' import 'package:PiliPlus/pages/setting/widgets/select_dialog.dart'; import 'package:PiliPlus/plugin/pl_player/models/audio_output_type.dart'; import 'package:PiliPlus/plugin/pl_player/models/hwdec_type.dart'; +import 'package:PiliPlus/utils/filtering_text.dart'; import 'package:PiliPlus/utils/storage.dart'; import 'package:PiliPlus/utils/storage_key.dart'; import 'package:PiliPlus/utils/storage_pref.dart'; import 'package:PiliPlus/utils/video_utils.dart'; import 'package:flutter/foundation.dart' show kDebugMode; import 'package:flutter/material.dart'; +import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; import 'package:material_design_icons_flutter/material_design_icons_flutter.dart'; @@ -102,6 +104,20 @@ List get videoSettings => [ getSubtitle: () => '当前:${Pref.audioOutput}', onTap: _showAudioOutputDialog, ), + NormalModel( + title: '缓冲大小', + leading: const Icon(Icons.storage_outlined), + getSubtitle: () => + '当前:${Pref.bufferSize}MB。同时为前向和后向缓冲区大小。对于直播流,无后向缓冲大小,全部转给前向(此选项即mpv的--demuxer-max-bytes,--demuxer-max-back-bytes)', + onTap: _showBufferSizeDialog, + ), + NormalModel( + title: '缓冲时长', + leading: const Icon(Icons.av_timer), + getSubtitle: () => + '当前:${Pref.bufferSec}s。实际缓冲为二者最小值。对于直播流,该选项无效(此选项即mpv的--cache-secs)', + onTap: _showBufferSecDialog, + ), NormalModel( title: '视频同步', leading: const Icon(Icons.view_timeline_outlined), @@ -399,3 +415,70 @@ Future _showHwDecDialog( setState(); } } + +void _showDecimalDialog( + BuildContext context, + VoidCallback setState, { + required String key, + required double defVal, + required Widget title, + required InputDecoration? decoration, +}) { + String value = (GStorage.setting.get(key) ?? defVal).toString(); + showDialog( + context: context, + builder: (context) => AlertDialog( + title: title, + content: TextFormField( + autofocus: true, + initialValue: value, + keyboardType: const .numberWithOptions(decimal: true), + onChanged: (val) => value = val, + inputFormatters: FilteringText.decimal, + decoration: decoration, + ), + actions: [ + TextButton( + onPressed: Get.back, + child: Text( + '取消', + style: TextStyle(color: ColorScheme.of(context).outline), + ), + ), + TextButton( + onPressed: () async { + try { + final val = double.parse(value); + Get.back(); + await GStorage.setting.put(key, val); + setState(); + } catch (e) { + SmartDialog.showToast(e.toString()); + } + }, + child: const Text('确定'), + ), + ], + ), + ); +} + +void _showBufferSizeDialog(BuildContext context, VoidCallback setState) => + _showDecimalDialog( + context, + setState, + key: SettingBoxKey.bufferSize, + defVal: Pref.bufferSize, + title: const Text('缓冲大小'), + decoration: const InputDecoration(suffixText: 'MB'), + ); + +void _showBufferSecDialog(BuildContext context, VoidCallback setState) => + _showDecimalDialog( + context, + setState, + key: SettingBoxKey.bufferSec, + defVal: Pref.bufferSec, + title: const Text('缓冲时长'), + decoration: const InputDecoration(suffixText: 's'), + ); diff --git a/lib/pages/setting/pages/play_speed_set.dart b/lib/pages/setting/pages/play_speed_set.dart index 7eab065a2..2c9b0cf3c 100644 --- a/lib/pages/setting/pages/play_speed_set.dart +++ b/lib/pages/setting/pages/play_speed_set.dart @@ -4,11 +4,11 @@ import 'package:PiliPlus/common/widgets/flutter/list_tile.dart'; import 'package:PiliPlus/common/widgets/scaffold.dart'; import 'package:PiliPlus/common/widgets/view_safe_area.dart'; import 'package:PiliPlus/utils/extension/context_ext.dart'; +import 'package:PiliPlus/utils/filtering_text.dart'; import 'package:PiliPlus/utils/storage.dart'; import 'package:PiliPlus/utils/storage_key.dart'; import 'package:PiliPlus/utils/storage_pref.dart'; import 'package:flutter/material.dart' hide ListTile; -import 'package:flutter/services.dart' show FilteringTextInputFormatter; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; import 'package:hive_ce/hive.dart'; @@ -73,9 +73,7 @@ class _PlaySpeedPageState extends State { border: OutlineInputBorder(borderRadius: .all(.circular(6))), ), onChanged: (value) => initialValue = value, - inputFormatters: [ - FilteringTextInputFormatter.allow(RegExp(r'[\d\.]+')), - ], + inputFormatters: FilteringText.decimal, ), ], ), diff --git a/lib/pages/video/widgets/header_control.dart b/lib/pages/video/widgets/header_control.dart index c15aeca9f..54e04869e 100644 --- a/lib/pages/video/widgets/header_control.dart +++ b/lib/pages/video/widgets/header_control.dart @@ -809,12 +809,6 @@ class HeaderControlState extends State onTap: () => Utils.copyText('VideoTrack\n${state.track.audio}'), ), - ListTile( - dense: true, - title: const Text("pitch"), - subtitle: Text(state.pitch.toString()), - onTap: () => Utils.copyText('pitch\n${state.pitch}'), - ), ListTile( dense: true, title: const Text("rate"), diff --git a/lib/plugin/pl_player/controller.dart b/lib/plugin/pl_player/controller.dart index 543ed1257..ee972383f 100644 --- a/lib/plugin/pl_player/controller.dart +++ b/lib/plugin/pl_player/controller.dart @@ -597,7 +597,6 @@ class PlPlayerController with BlockConfigMixin { final player = await Player.create( configuration: PlayerConfiguration( - bufferSize: isLive ? 16 * 1024 * 1024 : 4 * 1024 * 1024, logLevel: kDebugMode ? .warn : .error, options: opt, ), @@ -614,17 +613,38 @@ class PlPlayerController with BlockConfigMixin { ), ); - player.setMediaHeader( - userAgent: BrowserUa.pc, - referer: HttpString.baseUrl, - ); - // await player.setAudioTrack(.auto()); + player.setMediaHeader(userAgent: BrowserUa.pc, referer: HttpString.baseUrl); _startListeners(player); return player; } + Map? _buffer; + Map get buffer => _buffer ??= _initBuffer(); + Map? _liveBuffer; + Map get liveBuffer => _liveBuffer ??= _initLiveBuffer(); + + Map _initBuffer() { + final bufSec = Pref.bufferSec * _playbackSpeed.value; + final bufSiz = (Pref.bufferSize * 0x100000).toStringAsFixed(0); + return { + 'cache': 'yes', + 'cache-secs': bufSec.toStringAsFixed(3), + 'demuxer-hysteresis-secs': (bufSec / 1.5).toStringAsFixed(3), + 'demuxer-max-bytes': bufSiz, + 'demuxer-max-back-bytes': bufSiz, + }; + } + + Map _initLiveBuffer() { + return { + 'cache': 'yes', + 'demuxer-max-bytes': (Pref.bufferSize * 0x200000).toStringAsFixed(0), + 'demuxer-max-back-bytes': '0', + }; + } + // 配置播放器 Future _createVideoController( DataSource dataSource, @@ -654,6 +674,16 @@ class PlPlayerController with BlockConfigMixin { final Map extras = {}; + if (dataSource is FileSource) { + extras['cache'] = 'no'; + } else { + if (isLive) { + extras.addAll(liveBuffer); + } else { + extras.addAll(buffer); + } + } + String video = dataSource.videoSource; if (dataSource.audioSource case final audio? when (audio.isNotEmpty)) { if (onlyPlayAudio.value) { @@ -678,11 +708,8 @@ class PlPlayerController with BlockConfigMixin { if (dataSource is FileSource) { return null; } - if (_videoPlayerController?.current.isNotEmpty ?? false) { - return _videoPlayerController!.open( - _videoPlayerController!.current.last.copyWith(start: position), - play: true, - ); + if (_videoPlayerController case final ctr? when (ctr.current.isNotEmpty)) { + return ctr.open(ctr.current.last.copyWith(start: position), play: true); } return null; } diff --git a/lib/utils/filtering_text.dart b/lib/utils/filtering_text.dart new file mode 100644 index 000000000..649732b81 --- /dev/null +++ b/lib/utils/filtering_text.dart @@ -0,0 +1,7 @@ +import 'package:flutter/services.dart'; + +abstract final class FilteringText { + static final decimal = [ + FilteringTextInputFormatter.allow(RegExp(r'[\d\.]+')), + ]; +} diff --git a/lib/utils/storage_key.dart b/lib/utils/storage_key.dart index cf262c41f..542fb64c8 100644 --- a/lib/utils/storage_key.dart +++ b/lib/utils/storage_key.dart @@ -9,6 +9,8 @@ abstract final class SettingBoxKey { secondDecode = 'secondDecode', defaultPicQa = 'defaultPicQa', audioOutput = 'audioOutput', + bufferSize = 'bufferSize', + bufferSec = 'bufferSec', hardwareDecoding = 'hardwareDecoding', videoSync = 'videoSync', enableOnlineTotal = 'enableOnlineTotal', diff --git a/lib/utils/storage_pref.dart b/lib/utils/storage_pref.dart index 615860bb3..498137687 100644 --- a/lib/utils/storage_pref.dart +++ b/lib/utils/storage_pref.dart @@ -429,6 +429,12 @@ abstract final class Pref { defaultValue: PlatformUtils.isMobile ? 5 : 6, ); + static double get bufferSize => + _setting.get(SettingBoxKey.bufferSize, defaultValue: 4.0); + + static double get bufferSec => + _setting.get(SettingBoxKey.bufferSec, defaultValue: 16.0); + static String get audioOutput => _setting.get( SettingBoxKey.audioOutput, defaultValue: AudioOutput.defaultValue, diff --git a/pubspec.lock b/pubspec.lock index 9767bf83b..e3d2a57aa 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1077,7 +1077,7 @@ packages: description: path: media_kit ref: "version_1.2.5" - resolved-ref: e6c3481025959a23c259aa6072a28cebaa1c0fcc + resolved-ref: deac6b62569584b6a5e28e6c60c187a0a7281b3a url: "https://github.com/My-Responsitories/media-kit.git" source: git version: "1.1.11" @@ -1086,7 +1086,7 @@ packages: description: path: "libs/android/media_kit_libs_android_video" ref: "version_1.2.5" - resolved-ref: e6c3481025959a23c259aa6072a28cebaa1c0fcc + resolved-ref: deac6b62569584b6a5e28e6c60c187a0a7281b3a url: "https://github.com/My-Responsitories/media-kit.git" source: git version: "1.3.7" @@ -1095,7 +1095,7 @@ packages: description: path: "libs/ios/media_kit_libs_ios_video" ref: "version_1.2.5" - resolved-ref: e6c3481025959a23c259aa6072a28cebaa1c0fcc + resolved-ref: deac6b62569584b6a5e28e6c60c187a0a7281b3a url: "https://github.com/My-Responsitories/media-kit.git" source: git version: "1.1.4" @@ -1120,7 +1120,7 @@ packages: description: path: "libs/universal/media_kit_libs_video" ref: "version_1.2.5" - resolved-ref: e6c3481025959a23c259aa6072a28cebaa1c0fcc + resolved-ref: deac6b62569584b6a5e28e6c60c187a0a7281b3a url: "https://github.com/My-Responsitories/media-kit.git" source: git version: "1.0.5" @@ -1129,7 +1129,7 @@ packages: description: path: "libs/windows/media_kit_libs_windows_video" ref: "version_1.2.5" - resolved-ref: e6c3481025959a23c259aa6072a28cebaa1c0fcc + resolved-ref: deac6b62569584b6a5e28e6c60c187a0a7281b3a url: "https://github.com/My-Responsitories/media-kit.git" source: git version: "1.0.10" @@ -1138,7 +1138,7 @@ packages: description: path: media_kit_native_event_loop ref: "version_1.2.5" - resolved-ref: e6c3481025959a23c259aa6072a28cebaa1c0fcc + resolved-ref: deac6b62569584b6a5e28e6c60c187a0a7281b3a url: "https://github.com/My-Responsitories/media-kit.git" source: git version: "1.0.9" @@ -1147,7 +1147,7 @@ packages: description: path: media_kit_video ref: "version_1.2.5" - resolved-ref: e6c3481025959a23c259aa6072a28cebaa1c0fcc + resolved-ref: deac6b62569584b6a5e28e6c60c187a0a7281b3a url: "https://github.com/My-Responsitories/media-kit.git" source: git version: "1.2.5"