feat(player): support playback speeds up to 8x

close #1545
This commit is contained in:
edde746
2026-07-26 04:24:55 +02:00
parent 2a25f21e27
commit 3b1e71b3fa
12 changed files with 194 additions and 16 deletions
+3
View File
@@ -0,0 +1,3 @@
/// Inclusive playback-rate bounds supported by every in-app player backend.
const double minimumPlaybackRate = 0.25;
const double maximumPlaybackRate = 8.0;
+2 -1
View File
@@ -1,6 +1,7 @@
import 'dart:io' show Platform;
import '../../media/media_display_criteria.dart';
import '../../media/playback_rate.dart';
import '../models.dart';
import 'platform/player_android.dart';
import 'player_native.dart';
@@ -159,7 +160,7 @@ abstract class Player {
/// Set the playback rate/speed.
///
/// [rate] - Playback rate from 0.25 to 4.0 (1.0 = normal speed).
/// [rate] - Playback rate from [minimumPlaybackRate] to [maximumPlaybackRate] (1.0 = normal speed).
Future<void> setRate(double rate);
/// Set the audio output device.
+3 -2
View File
@@ -3,6 +3,7 @@ import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import '../media/playback_rate.dart';
import '../models/hotkey_model.dart';
import '../i18n/strings.g.dart';
import '../mpv/mpv.dart';
@@ -412,12 +413,12 @@ class KeyboardShortcutsService extends ChangeNotifier {
onPreviousEpisode?.call();
break;
case 'speed_increase':
final newRateUp = (player.state.rate + 0.25).clamp(0.25, 3.0);
final newRateUp = (player.state.rate + 0.25).clamp(minimumPlaybackRate, maximumPlaybackRate);
player.setRate(newRateUp);
_settingsService.write(SettingsService.defaultPlaybackSpeed, newRateUp);
break;
case 'speed_decrease':
final newRateDown = (player.state.rate - 0.25).clamp(0.25, 3.0);
final newRateDown = (player.state.rate - 0.25).clamp(minimumPlaybackRate, maximumPlaybackRate);
player.setRate(newRateDown);
_settingsService.write(SettingsService.defaultPlaybackSpeed, newRateDown);
break;
+2 -1
View File
@@ -1,5 +1,6 @@
import 'dart:convert';
import '../media/ids.dart';
import '../media/playback_rate.dart';
import '../media/media_version_preference.dart';
import 'dart:io';
import 'package:flutter/foundation.dart';
@@ -476,7 +477,7 @@ class SettingsService extends BaseSharedPreferencesService {
static final defaultPlaybackSpeed = DoublePref(
'default_playback_speed',
defaultValue: 1.0,
transform: (v) => v.clamp(0.5, 3.0),
transform: (v) => v.clamp(minimumPlaybackRate, maximumPlaybackRate),
);
static final defaultBoxFitMode = IntPref('default_box_fit_mode', transform: (v) => v.clamp(0, 2));
static final displaySwitchDelay = IntPref('display_switch_delay', transform: (v) => v.clamp(0, 10));
@@ -1,6 +1,7 @@
import 'dart:async';
import 'dart:math';
import '../../media/playback_rate.dart';
import '../../utils/app_logger.dart';
import '../models/playback_state.dart';
import '../models/watch_session.dart';
@@ -60,8 +61,8 @@ class HostPlaybackCoordinator {
static const int seekDebounceMs = 200;
static const int implicitJumpThresholdMs = 1500;
static const int selfRecoveryMinBufferAheadMs = 2000;
static const double _minimumRemoteRate = 0.25;
static const double _maximumRemoteRate = 4.0;
static const double _minimumRemoteRate = minimumPlaybackRate;
static const double _maximumRemoteRate = maximumPlaybackRate;
final String myPeerId;
final void Function(PlaybackState state, {String? toPeerId}) _sendState;
@@ -13,6 +13,7 @@ import 'package:provider/provider.dart';
import '../../../models/shader_preset.dart';
import '../../../models/transcode_quality_preset.dart';
import '../../../media/playback_rate.dart';
import '../../../media/media_version.dart';
import '../../../mpv/mpv.dart';
import '../../../providers/shader_provider.dart';
@@ -765,7 +766,26 @@ class _VideoSettingsSheetState extends State<VideoSettingsSheet> {
initialData: widget.player.state.rate,
builder: (context, snapshot) {
final currentRate = snapshot.data ?? 1.0;
final speeds = [0.5, 0.75, 1.0, 1.25, 1.5, 1.75, 2.0, 2.25, 2.5, 2.75, 3.0];
const speeds = <double>[
0.5,
0.75,
1.0,
1.25,
1.5,
1.75,
2.0,
2.25,
2.5,
2.75,
3.0,
3.5,
4.0,
4.5,
5.0,
6.0,
7.0,
maximumPlaybackRate,
];
return ListView.builder(
itemCount: speeds.length,