fix(macos): reduce idle CPU when video player is paused

- Remove vulkan-swap-mode mailbox (continuous GPU rendering even when paused)
- End playback activity assertion on pause, restore on play
- Fix PerformanceStatsService persistent frame callback leak
This commit is contained in:
edde746
2026-03-12 23:22:58 +01:00
parent efd28d3ca3
commit 3d30ea1342
3 changed files with 24 additions and 2 deletions
@@ -33,6 +33,8 @@ class PerformanceStatsService {
int _frameCount = 0;
DateTime _lastFpsUpdate = DateTime.now();
double? _currentUiFps;
bool _fpsTrackingActive = false;
bool _fpsCallbackRegistered = false;
// Track runtime player type for logging (can differ from Dart object type after fallback)
// Values: 'exoplayer', 'mpv', or 'unknown'
@@ -77,11 +79,16 @@ class PerformanceStatsService {
void _startFpsTracking() {
_frameCount = 0;
_lastFpsUpdate = DateTime.now();
SchedulerBinding.instance.addPersistentFrameCallback(_onFrame);
_fpsTrackingActive = true;
if (!_fpsCallbackRegistered) {
_fpsCallbackRegistered = true;
SchedulerBinding.instance.addPersistentFrameCallback(_onFrame);
}
}
/// Called every frame to count FPS.
void _onFrame(Duration timestamp) {
if (!_fpsTrackingActive) return;
_frameCount++;
final now = DateTime.now();
final elapsed = now.difference(_lastFpsUpdate);
@@ -96,6 +103,8 @@ class PerformanceStatsService {
void stopPolling() {
_pollingTimer?.cancel();
_pollingTimer = null;
_fpsTrackingActive = false;
_currentUiFps = null;
}
/// Fetch all performance stats from the player.
+13 -1
View File
@@ -85,7 +85,7 @@ class MpvPlayerCore: MpvPlayerCoreBase {
override func configurePlatformMpvOptions() {
guard let mpv else { return }
checkError(mpv_set_option_string(mpv, "ao", "avfoundation,coreaudio"))
checkError(mpv_set_option_string(mpv, "vulkan-swap-mode", "mailbox"))
// Default fifo (vsync) mode mailbox was causing continuous GPU rendering even when paused
}
var videoLayer: CAMetalLayer? { metalLayer }
@@ -113,9 +113,13 @@ class MpvPlayerCore: MpvPlayerCoreBase {
command(["seek", "0", "relative+exact"])
}
private var isVisible = false
func setVisible(_ visible: Bool) {
guard let metalLayer, !isPipActive else { return }
isVisible = visible
if visible {
metalLayer.removeFromSuperlayer()
if let superlayer = window?.contentView?.layer {
@@ -130,6 +134,14 @@ class MpvPlayerCore: MpvPlayerCoreBase {
print("[MpvPlayerCore] setVisible(\(visible))")
}
func setPaused(_ paused: Bool) {
if paused {
endPlaybackActivity()
} else if isVisible {
beginPlaybackActivity()
}
}
func updateFrame(_ frame: CGRect? = nil) {
guard let metalLayer, !isPipActive else { return }
@@ -277,6 +277,7 @@ class MpvPlayerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, MpvPluginS
if name == "pause" {
let isPlaying = value == "no"
pipController?.setPlaying(isPlaying)
playerCore?.setPaused(!isPlaying)
}
result(nil)