feat(android): tune frame rate and subtitle rendering

This commit is contained in:
edde746
2026-06-24 01:34:03 +02:00
parent c9dc15b1d8
commit 21c4ab430e
15 changed files with 187 additions and 37 deletions
+19 -1
View File
@@ -1,6 +1,8 @@
import 'package:collection/collection.dart';
import 'package:flutter/services.dart';
import '../../../services/device_performance.dart';
import '../../../services/settings_service.dart';
import '../../models.dart';
import '../player_base.dart';
@@ -98,6 +100,14 @@ class PlayerAndroid extends PlayerBase {
'tunnelingEnabled': _tunnelingEnabled,
'dvConversionMode': _dvConversionMode,
'audioPassthroughEnabled': _audioPassthroughEnabled,
// Cheap (32-bit) TV boxes run the hardware video path a frame behind a GL
// subtitle overlay; render the ASS one frame earlier there to realign.
'assVideoLatencyFrames': DevicePerformance.isLowEndHardware ? 1 : 0,
// libass overlay raster scale from the "Render Resolution" subtitle setting
// (Full / ¾ / ½ / ⅓ / ¼); < 1 trades sharpness for throughput on slow GPUs.
'subtitleRenderScale': SettingsService.instance
.read(SettingsService.subtitleRenderResolution)
.androidRenderScale,
});
if (result != true) {
throw Exception('Failed to initialize ExoPlayer');
@@ -459,12 +469,20 @@ class PlayerAndroid extends PlayerBase {
}
@override
Future<bool> setVideoFrameRate(double fps, int durationMs, {int extraDelayMs = 0}) async {
Future<bool> setVideoFrameRate(
double fps,
int durationMs, {
int extraDelayMs = 0,
int videoWidth = 0,
int videoHeight = 0,
}) async {
if (disposed || !initialized) return false;
final result = await invoke<bool>('setVideoFrameRate', {
'fps': fps,
'duration': durationMs,
'extraDelayMs': extraDelayMs,
'videoWidth': videoWidth,
'videoHeight': videoHeight,
});
return result ?? false;
}
+7 -1
View File
@@ -254,7 +254,13 @@ abstract class Player {
/// the caller is responsible for starting playback itself.
///
/// On other platforms, this is a no-op that returns `false`.
Future<bool> setVideoFrameRate(double fps, int durationMs, {int extraDelayMs = 0});
Future<bool> setVideoFrameRate(
double fps,
int durationMs, {
int extraDelayMs = 0,
int videoWidth = 0,
int videoHeight = 0,
});
/// Clear the video frame rate hint and restore default display mode.
///
+7 -1
View File
@@ -613,7 +613,13 @@ abstract class PlayerBase with PlayerStreamControllersMixin implements Player {
Future<void> updateFrame() async {}
@override
Future<bool> setVideoFrameRate(double fps, int durationMs, {int extraDelayMs = 0}) async => false;
Future<bool> setVideoFrameRate(
double fps,
int durationMs, {
int extraDelayMs = 0,
int videoWidth = 0,
int videoHeight = 0,
}) async => false;
@override
// ignore: no-empty-block - base no-op, overridden by platform subclasses
+10 -4
View File
@@ -7,9 +7,7 @@ import '../../media/media_display_criteria.dart';
import '../models.dart';
import 'player_base.dart';
/// Shared native implementation of [Player] for iOS, macOS, Android (MPV fallback), and Linux.
/// Uses MPVKit via platform channels with Metal rendering (Apple), native window (Android),
/// or FlTextureGL (Linux).
/// MPV-backed player for platforms where AetherEngine is not the native route.
class PlayerNative extends PlayerBase {
int? _textureIdValue;
String _dvConversionMode = 'auto';
@@ -403,12 +401,20 @@ class PlayerNative extends PlayerBase {
}
@override
Future<bool> setVideoFrameRate(double fps, int durationMs, {int extraDelayMs = 0}) async {
Future<bool> setVideoFrameRate(
double fps,
int durationMs, {
int extraDelayMs = 0,
int videoWidth = 0,
int videoHeight = 0,
}) async {
if (!Platform.isAndroid || disposed || !initialized) return false;
final result = await invoke<bool>('setVideoFrameRate', {
'fps': fps,
'duration': durationMs,
'extraDelayMs': extraDelayMs,
'videoWidth': videoWidth,
'videoHeight': videoHeight,
});
return result ?? false;
}