Calling MFScheduleWorkItemEx() schedules the operation in the timer queue, but unless GetParameters() returns the timer queue, callback invocation will occur in the standard queue.
From: Conor McCarthy cmccarthy@codeweavers.com
Calling MFScheduleWorkItemEx() schedules the operation in the timer queue, but unless GetParameters() returns the timer queue, callback invocation will occur in the standard queue. --- dlls/mf/clock.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-)
diff --git a/dlls/mf/clock.c b/dlls/mf/clock.c index e6be05d2794..f0e8dbdf0bd 100644 --- a/dlls/mf/clock.c +++ b/dlls/mf/clock.c @@ -1098,6 +1098,13 @@ static ULONG WINAPI present_clock_timer_callback_Release(IMFAsyncCallback *iface return IMFPresentationClock_Release(&clock->IMFPresentationClock_iface); }
+static HRESULT WINAPI present_clock_timer_callback_GetParameters(IMFAsyncCallback *iface, DWORD *flags, DWORD *queue) +{ + *flags = 0; + *queue = MFASYNC_CALLBACK_QUEUE_TIMER; + return S_OK; +} + static HRESULT WINAPI present_clock_timer_callback_Invoke(IMFAsyncCallback *iface, IMFAsyncResult *result) { struct presentation_clock *clock = impl_from_timer_callback_IMFAsyncCallback(iface); @@ -1133,7 +1140,7 @@ static const IMFAsyncCallbackVtbl presentclocktimercallbackvtbl = present_clock_callback_QueryInterface, present_clock_timer_callback_AddRef, present_clock_timer_callback_Release, - present_clock_callback_GetParameters, + present_clock_timer_callback_GetParameters, present_clock_timer_callback_Invoke, };
From: Conor McCarthy cmccarthy@codeweavers.com
--- dlls/mf/samplegrabber.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/dlls/mf/samplegrabber.c b/dlls/mf/samplegrabber.c index de599139736..c46cd8affab 100644 --- a/dlls/mf/samplegrabber.c +++ b/dlls/mf/samplegrabber.c @@ -744,7 +744,9 @@ static ULONG WINAPI sample_grabber_stream_timer_callback_Release(IMFAsyncCallbac static HRESULT WINAPI sample_grabber_stream_timer_callback_GetParameters(IMFAsyncCallback *iface, DWORD *flags, DWORD *queue) { - return E_NOTIMPL; + *flags = 0; + *queue = MFASYNC_CALLBACK_QUEUE_TIMER; + return S_OK; }
static HRESULT WINAPI sample_grabber_stream_timer_callback_Invoke(IMFAsyncCallback *iface, IMFAsyncResult *result)
Have you tested this manually somehow? I'm not sure why this is necessary, maybe only to unload some callbacks from standard queue?
On Tue Apr 1 01:18:18 2025 +0000, Nikolay Sivov wrote:
Have you tested this manually somehow? I'm not sure why this is necessary, maybe only to unload some callbacks from standard queue?
I tested mp4 playback in Windows. All video and audio work is done in one thread, except for `OnProcessSample()`, which is called from another thread. We can replicate this by ensuring the timer queue is used. This doesn't cover the case where `MF_SAMPLEGRABBERSINK_IGNORE_CLOCK` is set, and I haven't tested it. Do games use that flag rarely?
Yes, the main reason is to unload the standard queue. If the game's `OnProcessSample()` takes a bit of time, this change makes it less likely that frames will take too long to decode, which can happen with 4K, but I don't have a example of that happening in a game which uses the session engine.
Actually I think this is an rtworkq issue. `invoke_async_callback()` should take as a second parameter the default queue, which should be TIMER if the callback is invoked as a result of `RtwqPutWaitingWorkItem()`, `RtwqScheduleWorkItem()` or `RtwqAddPeriodicCallback()`. Does that sound right?
On Tue Apr 8 16:31:42 2025 +0000, Conor McCarthy wrote:
Actually I think this is an rtworkq issue. `invoke_async_callback()` should take as a second parameter the default queue, which should be TIMER if the callback is invoked as a result of `RtwqPutWaitingWorkItem()`, `RtwqScheduleWorkItem()` or `RtwqAddPeriodicCallback()`. Does that sound right?
Where do you see this? It's a two step process. First item should trigger and that's where QUEUE_TIMER is used, but where payload is executed is determined by GetParameters() essentially, with standard queue being a default one. Now in context of the media session, docs talk about explicit per topology branch queues. User queues do behave differently, using MFPutWorkItem() with a user queue makes Invoke happens consistently on another thread.
Current logic we have is almost like that except that MFPutWorkItem() always uses the queue it was told to use.
On Tue Apr 8 16:31:42 2025 +0000, Nikolay Sivov wrote:
Where do you see this? It's a two step process. First item should trigger and that's where QUEUE_TIMER is used, but where payload is executed is determined by GetParameters() essentially, with standard queue being a default one. Now in context of the media session, docs talk about explicit per topology branch queues. User queues do behave differently, using MFPutWorkItem() with a user queue makes Invoke happens consistently on another thread. Current logic we have is almost like that except that MFPutWorkItem() always uses the queue it was told to use.
I tested this yesterday on Windows and the current rtworkq code is correct, yes. I also modified this MR to create a user queue for MF session sinks. I'll push that tomorrow. If you believe these changes are not a good idea, they're not needed to fix anything specific at the moment.