MF Media Engine on Windows will only send one seek request at a time. It will queue the `seekTime` value of the most recent `SetCurrentTime` call and forward only that request to the Media Session after it completes its current seek (i.e. when it sends out the `MF_MEDIA_ENGINE_EVENT_SEEKED` notify event).
Wine currently forwards every `SetCurrentTime` call to the Media Session, which can result in a large queue of seeks, one performed after the other, resulting in a delay before completing the final seek request.
This fixes slow seek times in some worlds in VRChat.
From: Brendan McGrath bmcgrath@codeweavers.com
--- dlls/mfmediaengine/main.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+)
diff --git a/dlls/mfmediaengine/main.c b/dlls/mfmediaengine/main.c index 27249f09fa6..51d89bde79b 100644 --- a/dlls/mfmediaengine/main.c +++ b/dlls/mfmediaengine/main.c @@ -153,6 +153,7 @@ struct media_engine double default_playback_rate; double volume; double duration; + double next_seek; MF_MEDIA_ENGINE_NETWORK network_state; MF_MEDIA_ENGINE_ERR error_code; HRESULT extended_code; @@ -911,6 +912,8 @@ static HRESULT WINAPI media_engine_callback_GetParameters(IMFAsyncCallback *ifac return E_NOTIMPL; }
+static HRESULT media_engine_set_current_time(struct media_engine *engine, double seektime); + static HRESULT WINAPI media_engine_session_events_Invoke(IMFAsyncCallback *iface, IMFAsyncResult *result) { struct media_engine *engine = impl_from_session_events_IMFAsyncCallback(iface); @@ -989,6 +992,8 @@ static HRESULT WINAPI media_engine_session_events_Invoke(IMFAsyncCallback *iface media_engine_set_flag(engine, FLAGS_ENGINE_SEEKING | FLAGS_ENGINE_IS_ENDED, FALSE); IMFMediaEngineNotify_EventNotify(engine->callback, MF_MEDIA_ENGINE_EVENT_SEEKED, 0, 0); IMFMediaEngineNotify_EventNotify(engine->callback, MF_MEDIA_ENGINE_EVENT_TIMEUPDATE, 0, 0); + if (isfinite(engine->next_seek)) + media_engine_set_current_time(engine, engine->next_seek); } LeaveCriticalSection(&engine->cs); IMFMediaEngineNotify_EventNotify(engine->callback, MF_MEDIA_ENGINE_EVENT_PLAYING, 0, 0); @@ -1865,6 +1870,14 @@ static HRESULT media_engine_set_current_time(struct media_engine *engine, double if (FAILED(hr) || !(caps & MFSESSIONCAP_SEEK)) return hr;
+ if (engine->flags & FLAGS_ENGINE_SEEKING) + { + engine->next_seek = seektime; + return S_OK; + } + + engine->next_seek = NAN; + position.vt = VT_I8; position.hVal.QuadPart = min(max(0, seektime), engine->duration) * 10000000;
@@ -3378,6 +3391,7 @@ static HRESULT init_media_engine(DWORD flags, IMFAttributes *attributes, struct engine->playback_rate = 1.0; engine->volume = 1.0; engine->duration = NAN; + engine->next_seek = NAN; engine->video_frame.pts = MINLONGLONG; InitializeCriticalSection(&engine->cs);
This makes sense. Have you checked if media session itself does similar kind of throttling on Windows?
On Tue Jun 24 20:56:25 2025 +0000, Nikolay Sivov wrote:
This makes sense. Have you checked if media session itself does similar kind of throttling on Windows?
I did yes. I made multiple calls to `IMFMediaSession::Start` in a row and found they all reached the `IMFMediaSource`. But if I called `IMFMediaEngine::SetCurrentTime` multiple times in a row, only the first and last made it to the `IMFMediaSource`.
Nikolay Sivov (@nsivov) commented about dlls/mfmediaengine/main.c:
engine->playback_rate = 1.0; engine->volume = 1.0; engine->duration = NAN;
- engine->next_seek = NAN; engine->video_frame.pts = MINLONGLONG;
NAN was used for duration as a special marker because it doesn't come from user input. For the "next_seek" we could use another flag probably, instead of relying on this special value. Let me know which way looks better to you.
On Tue Jun 24 21:21:02 2025 +0000, Nikolay Sivov wrote:
NAN was used for duration as a special marker because it doesn't come from user input. For the "next_seek" we could use another flag probably, instead of relying on this special value. Let me know which way looks better to you.
I actually started with '-1', but then I thought maybe the user _could_ provide that. So I went with NAN for the same reason; it's not a value that you would expect from user input.
This merge request was approved by Nikolay Sivov.