@@ -282,8 +282,7 @@ class PlayerNative extends PlayerBase {
|
|||||||
Future<void> updateFrame() async {
|
Future<void> updateFrame() async {
|
||||||
checkDisposed();
|
checkDisposed();
|
||||||
if (!initialized) return;
|
if (!initialized) return;
|
||||||
// Only iOS and macOS use Metal layer that needs frame updates
|
if (Platform.isIOS || Platform.isMacOS || Platform.isLinux) {
|
||||||
if (Platform.isIOS || Platform.isMacOS) {
|
|
||||||
await methodChannel.invokeMethod('updateFrame');
|
await methodChannel.invokeMethod('updateFrame');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1698,6 +1698,10 @@ class VideoPlayerScreenState extends State<VideoPlayerScreen> with WidgetsBindin
|
|||||||
// Toggle wakelock based on playback state
|
// Toggle wakelock based on playback state
|
||||||
if (isPlaying) {
|
if (isPlaying) {
|
||||||
WakelockPlus.enable();
|
WakelockPlus.enable();
|
||||||
|
// Force a texture refresh on resume to unstick stale frames
|
||||||
|
// (Linux/macOS texture registrars can miss frame-available
|
||||||
|
// notifications after extended pause periods)
|
||||||
|
player?.updateFrame();
|
||||||
} else {
|
} else {
|
||||||
WakelockPlus.disable();
|
WakelockPlus.disable();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,6 +43,9 @@ class PlaybackProgressTracker {
|
|||||||
/// Timer ticks to skip before retrying after failures (exponential backoff).
|
/// Timer ticks to skip before retrying after failures (exponential backoff).
|
||||||
int _ticksToSkip = 0;
|
int _ticksToSkip = 0;
|
||||||
|
|
||||||
|
/// Counts timer ticks while paused to send periodic "paused" heartbeats.
|
||||||
|
int _pausedTickCounter = 0;
|
||||||
|
|
||||||
PlaybackProgressTracker({
|
PlaybackProgressTracker({
|
||||||
required this.client,
|
required this.client,
|
||||||
required this.metadata,
|
required this.metadata,
|
||||||
@@ -70,6 +73,7 @@ class PlaybackProgressTracker {
|
|||||||
|
|
||||||
_progressTimer = Timer.periodic(updateInterval, (timer) {
|
_progressTimer = Timer.periodic(updateInterval, (timer) {
|
||||||
if (player.state.playing) {
|
if (player.state.playing) {
|
||||||
|
_pausedTickCounter = 0;
|
||||||
// Skip ticks when backing off after consecutive failures to avoid
|
// Skip ticks when backing off after consecutive failures to avoid
|
||||||
// flooding the network with doomed requests during an outage.
|
// flooding the network with doomed requests during an outage.
|
||||||
if (_ticksToSkip > 0) {
|
if (_ticksToSkip > 0) {
|
||||||
@@ -77,6 +81,18 @@ class PlaybackProgressTracker {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_sendProgress('playing');
|
_sendProgress('playing');
|
||||||
|
} else {
|
||||||
|
// Send periodic "paused" updates to keep the Plex session alive
|
||||||
|
// (~60s with default 10s interval)
|
||||||
|
_pausedTickCounter++;
|
||||||
|
if (_pausedTickCounter >= 6) {
|
||||||
|
_pausedTickCounter = 0;
|
||||||
|
if (_ticksToSkip > 0) {
|
||||||
|
_ticksToSkip--;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_sendProgress('paused');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -324,6 +324,11 @@ static void mpv_plugin_handle_method_call(FlMethodChannel* channel,
|
|||||||
|
|
||||||
response = FL_METHOD_RESPONSE(fl_method_success_response_new(nullptr));
|
response = FL_METHOD_RESPONSE(fl_method_success_response_new(nullptr));
|
||||||
}
|
}
|
||||||
|
} else if (strcmp(method, "updateFrame") == 0) {
|
||||||
|
if (self->visible && self->texture) {
|
||||||
|
mpv_texture_mark_frame_available(self->texture);
|
||||||
|
}
|
||||||
|
response = FL_METHOD_RESPONSE(fl_method_success_response_new(nullptr));
|
||||||
} else if (strcmp(method, "isInitialized") == 0) {
|
} else if (strcmp(method, "isInitialized") == 0) {
|
||||||
gboolean initialized = self->player && self->initialized;
|
gboolean initialized = self->player && self->initialized;
|
||||||
response = FL_METHOD_RESPONSE(
|
response = FL_METHOD_RESPONSE(
|
||||||
|
|||||||
Reference in New Issue
Block a user