Open henrique-marques-present opened 9 months ago
I have noticed this problem before. However, I have no idea how to fix it :(
In Media Foundation API, it always force playing after seeking, even if the video paused before seeking. So I call pause() immediately after user calling seek() in paused video, but this seems cause problem.
I think the Media Foundation didn't take this situation into consideration.
When I playing video by built-in Windows Media Player (WMP)
, pause it, the video frame won't refresh after seeking.
I think WMP didn't call seek() when seeking paused video, just because the API doesn't support seeking in paused video.
One way to slightly improve this behavior is by implementing a debounce feature for the seek operation, which helps prevent rapid or unnecessary triggering. Wouldn't it be feasible to perform this debouncing on the plugin side?
// debounce duration
final Duration debounceTime = const Duration(milliseconds: 100);
// Debounce timer
Timer? _scrubDebounceTimer;
DateTime? _timerStartMoment;
// ...
/// Scrub the video and the animation with a debounce using a duration
/// of [debounceTime].
///
/// The debounce prevents the seek from being updated during any type of
/// scrub operation.
void scrub(Duration duration) {
// if is active create a new timer with the new seek value
if (_scrubDebounceTimer?.isActive ?? false) {
Duration elapsedTime = DateTime.now().difference(_timerStartMoment!);
// cancel the previous action
_scrubDebounceTimer!.cancel();
// evaluate the left duration to throw a seek event
final Duration leftDurationToUpdate = debounceTime - elapsedTime;
// re-recreate a timer with the new duration
_scrubDebounceTimer = Timer(leftDurationToUpdate, () => controller!.seekTo(duration);
return;
}
//
// If the timer is not active or not yet initialized, the timer
// and the datetime are initialize
//
_timerStartMoment = DateTime.now(); // time reference for when the [_seekDebounceTimer] started
_scrubDebounceTimer = Timer(debounceTime, () => controller!.seekTo(duration);
}
// ...
I prefer not to do it in plugin side because I think API should do only the essential task as possible. And it may be not a good idea if user just only want to call seek() once, in this case they can find the seekTo() called with a 100ms delay.
In UI level, developer can know if user just want to call seekTo() once (ex. click slider, press arrow key once) or user long-press arrow key / dragging slider to make multiple seekTo() calls. So I think the "delay" code should implement in UI code level, not in plugin side.
Media Foundation document has a page for scrub How to Perform Scrubbing.
Seeking, Fast Forward, and Reverse Play
In this example, seeking requests are queued but source code are complicated >.<
I think it is necessary to handle Media foundation's async events for better seeking(scrub) but complicated >.<
Oops... I used to think that video scrubbing was simply a series of fast seeks, but I didn't know that the key point was to display the current video frame after each seek operation.
It seems easy to implement according to the webpage you mentioned above, But, unfortunately... I tried to all SetRate(0), media foundation return 0 (OK) but video still playing... it seems SetRate(0) not working ( but SetRate(0.5) works ). Then I tried to call Pause() -> SetRate(0) -> a lot of Seek(ms)... frames not update after each seek operation...
So far I have no idea how to implemet it... orz
Issue:
When scrubbing through a video, it appears that the preview is stacking frames from the seek requests instead of displaying the correct frame corresponding to the scrubbed position. Is there a way to fix this issue with the Media Foundation API or this management has to be done on frontend?
Current behavior
https://github.com/jakky1/video_player_win/assets/147726319/a7afd232-dfa1-4cb8-bd12-458fd5869211
Code sample
Flutter doctor