feat: custom buffer size (#2370)

* feat: custom buffer size

* cache buffer params

---------

Co-authored-by: dom <githubaccount56556@proton.me>
This commit is contained in:
My-Responsitories
2026-06-10 01:54:32 +00:00
committed by dom
parent 3b942eeeaf
commit ce09306025
10 changed files with 149 additions and 38 deletions

View File

@@ -60,9 +60,7 @@ class _CachedNetworkSVGImageState extends State<CachedNetworkSVGImage> {
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() {

View File

@@ -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: [

View File

@@ -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<SettingsModel> 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<void> _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'),
);

View File

@@ -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<PlaySpeedPage> {
border: OutlineInputBorder(borderRadius: .all(.circular(6))),
),
onChanged: (value) => initialValue = value,
inputFormatters: [
FilteringTextInputFormatter.allow(RegExp(r'[\d\.]+')),
],
inputFormatters: FilteringText.decimal,
),
],
),

View File

@@ -809,12 +809,6 @@ class HeaderControlState extends State<HeaderControl>
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"),

View File

@@ -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<String, String>? _buffer;
Map<String, String> get buffer => _buffer ??= _initBuffer();
Map<String, String>? _liveBuffer;
Map<String, String> get liveBuffer => _liveBuffer ??= _initLiveBuffer();
Map<String, String> _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<String, String> _initLiveBuffer() {
return {
'cache': 'yes',
'demuxer-max-bytes': (Pref.bufferSize * 0x200000).toStringAsFixed(0),
'demuxer-max-back-bytes': '0',
};
}
// 配置播放器
Future<void> _createVideoController(
DataSource dataSource,
@@ -654,6 +674,16 @@ class PlPlayerController with BlockConfigMixin {
final Map<String, String> 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;
}

View File

@@ -0,0 +1,7 @@
import 'package:flutter/services.dart';
abstract final class FilteringText {
static final decimal = [
FilteringTextInputFormatter.allow(RegExp(r'[\d\.]+')),
];
}

View File

@@ -9,6 +9,8 @@ abstract final class SettingBoxKey {
secondDecode = 'secondDecode',
defaultPicQa = 'defaultPicQa',
audioOutput = 'audioOutput',
bufferSize = 'bufferSize',
bufferSec = 'bufferSec',
hardwareDecoding = 'hardwareDecoding',
videoSync = 'videoSync',
enableOnlineTotal = 'enableOnlineTotal',

View File

@@ -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,

View File

@@ -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"