Signed-off-by: Nikolay Sivov nsivov@codeweavers.com ---
v2: test inconsistently crashing on XP removed for now.
dlls/mfplat/mfplat.spec | 6 +- dlls/mfplat/queue.c | 194 +++++++++++++++++++++++++++++++++++-- dlls/mfplat/tests/mfplat.c | 48 +++++++++ include/mfapi.h | 4 + 4 files changed, 242 insertions(+), 10 deletions(-)
diff --git a/dlls/mfplat/mfplat.spec b/dlls/mfplat/mfplat.spec index c6234b006e..116ec61289 100644 --- a/dlls/mfplat/mfplat.spec +++ b/dlls/mfplat/mfplat.spec @@ -15,7 +15,7 @@ @ stub GetAMSubtypeFromD3DFormat @ stub GetD3DFormatFromMFSubtype @ stub LFGetGlobalPool -@ stub MFAddPeriodicCallback +@ stdcall MFAddPeriodicCallback(ptr ptr ptr) @ stdcall MFAllocateWorkQueue(ptr) @ stdcall MFAllocateWorkQueueEx(long ptr) @ stub MFAppendCollection @@ -100,7 +100,7 @@ @ stub MFGetSockaddrFromNumericName @ stub MFGetStrideForBitmapInfoHeader @ stdcall MFGetSystemTime() -@ stub MFGetTimerPeriodicity +@ stdcall MFGetTimerPeriodicity(ptr) @ stub MFGetUncompressedVideoFormat @ stub MFGetWorkQueueMMCSSClass @ stub MFGetWorkQueueMMCSSTaskId @@ -125,7 +125,7 @@ @ stdcall MFPutWorkItem(long ptr ptr) @ stdcall MFPutWorkItemEx(long ptr) @ stub MFRecordError -@ stub MFRemovePeriodicCallback +@ stdcall MFRemovePeriodicCallback(long) @ stdcall MFScheduleWorkItem(ptr ptr int64 ptr) @ stdcall MFScheduleWorkItemEx(ptr int64 ptr) @ stub MFSerializeAttributesToStream diff --git a/dlls/mfplat/queue.c b/dlls/mfplat/queue.c index f470cb44b5..e3b0687711 100644 --- a/dlls/mfplat/queue.c +++ b/dlls/mfplat/queue.c @@ -39,9 +39,14 @@ WINE_DEFAULT_DEBUG_CHANNEL(mfplat);
static LONG next_item_key;
+static MFWORKITEM_KEY get_item_key(DWORD mask, DWORD key) +{ + return ((MFWORKITEM_KEY)mask << 32) | key; +} + static MFWORKITEM_KEY generate_item_key(DWORD mask) { - return ((MFWORKITEM_KEY)mask << 32) | InterlockedIncrement(&next_item_key); + return get_item_key(mask, InterlockedIncrement(&next_item_key)); }
struct work_item @@ -315,9 +320,10 @@ void shutdown_system_queues(void) LeaveCriticalSection(&queues_section); }
-static void grab_work_item(struct work_item *item) +static struct work_item *grab_work_item(struct work_item *item) { InterlockedIncrement(&item->refcount); + return item; }
static void CALLBACK standard_queue_worker(TP_CALLBACK_INSTANCE *instance, void *context, TP_WORK *work) @@ -442,6 +448,15 @@ static void CALLBACK scheduled_item_cancelable_callback(TP_CALLBACK_INSTANCE *in release_work_item(item); }
+static void CALLBACK periodic_item_callback(TP_CALLBACK_INSTANCE *instance, void *context, TP_TIMER *timer) +{ + struct work_item *item = grab_work_item(context); + + invoke_async_callback(item->result); + + release_work_item(item); +} + static void queue_mark_item_pending(DWORD mask, struct work_item *item, MFWORKITEM_KEY *key) { *key = generate_item_key(mask); @@ -478,7 +493,8 @@ static HRESULT queue_submit_wait(struct queue *queue, HANDLE event, LONG priorit return S_OK; }
-static HRESULT queue_submit_timer(struct queue *queue, IMFAsyncResult *result, INT64 timeout, MFWORKITEM_KEY *key) +static HRESULT queue_submit_timer(struct queue *queue, IMFAsyncResult *result, INT64 timeout, DWORD period, + MFWORKITEM_KEY *key) { PTP_TIMER_CALLBACK callback; struct work_item *item; @@ -491,17 +507,19 @@ static HRESULT queue_submit_timer(struct queue *queue, IMFAsyncResult *result, I if (key) { queue_mark_item_pending(SCHEDULED_ITEM_KEY_MASK, item, key); - callback = scheduled_item_cancelable_callback; } + + if (period) + callback = periodic_item_callback; else - callback = scheduled_item_callback; + callback = key ? scheduled_item_cancelable_callback : scheduled_item_callback;
t.QuadPart = timeout * 1000 * 10; filetime.dwLowDateTime = t.u.LowPart; filetime.dwHighDateTime = t.u.HighPart;
item->u.timer_object = CreateThreadpoolTimer(callback, item, &queue->env); - SetThreadpoolTimer(item->u.timer_object, &filetime, 0, 0); + SetThreadpoolTimer(item->u.timer_object, &filetime, period, 0);
TRACE("dispatched %p.\n", result);
@@ -842,7 +860,7 @@ static HRESULT schedule_work_item(IMFAsyncResult *result, INT64 timeout, MFWORKI
TRACE("%p, %s, %p.\n", result, wine_dbgstr_longlong(timeout), key);
- hr = queue_submit_timer(queue, result, timeout, key); + hr = queue_submit_timer(queue, result, timeout, 0, key);
return hr; } @@ -912,3 +930,165 @@ HRESULT WINAPI MFCancelWorkItem(MFWORKITEM_KEY key)
return hr; } + +static DWORD get_timer_period(void) +{ + return 10; +} + +/*********************************************************************** + * MFGetTimerPeriodicity (mfplat.@) + */ +HRESULT WINAPI MFGetTimerPeriodicity(DWORD *period) +{ + TRACE("%p.\n", period); + + *period = get_timer_period(); + + return S_OK; +} + +struct periodic_callback +{ + IMFAsyncCallback IMFAsyncCallback_iface; + LONG refcount; + MFPERIODICCALLBACK callback; +}; + +static struct periodic_callback *impl_from_IMFAsyncCallback(IMFAsyncCallback *iface) +{ + return CONTAINING_RECORD(iface, struct periodic_callback, IMFAsyncCallback_iface); +} + +static HRESULT WINAPI periodic_callback_QueryInterface(IMFAsyncCallback *iface, REFIID riid, void **obj) +{ + if (IsEqualIID(riid, &IID_IMFAsyncCallback) || + IsEqualIID(riid, &IID_IUnknown)) + { + *obj = iface; + IMFAsyncCallback_AddRef(iface); + return S_OK; + } + + *obj = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI periodic_callback_AddRef(IMFAsyncCallback *iface) +{ + struct periodic_callback *callback = impl_from_IMFAsyncCallback(iface); + ULONG refcount = InterlockedIncrement(&callback->refcount); + + TRACE("%p, %u.\n", iface, refcount); + + return refcount; +} + +static ULONG WINAPI periodic_callback_Release(IMFAsyncCallback *iface) +{ + struct periodic_callback *callback = impl_from_IMFAsyncCallback(iface); + ULONG refcount = InterlockedDecrement(&callback->refcount); + + TRACE("%p, %u.\n", iface, refcount); + + if (!refcount) + heap_free(callback); + + return refcount; +} + +static HRESULT WINAPI periodic_callback_GetParameters(IMFAsyncCallback *iface, DWORD *flags, DWORD *queue) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI periodic_callback_Invoke(IMFAsyncCallback *iface, IMFAsyncResult *result) +{ + struct periodic_callback *callback = impl_from_IMFAsyncCallback(iface); + IUnknown *context = NULL; + + IMFAsyncResult_GetObject(result, &context); + + callback->callback(context); + + if (context) + IUnknown_Release(context); + + return S_OK; +} + +static const IMFAsyncCallbackVtbl periodic_callback_vtbl = +{ + periodic_callback_QueryInterface, + periodic_callback_AddRef, + periodic_callback_Release, + periodic_callback_GetParameters, + periodic_callback_Invoke, +}; + +static HRESULT create_periodic_callback_obj(MFPERIODICCALLBACK callback, IMFAsyncCallback **out) +{ + struct periodic_callback *object; + + object = heap_alloc(sizeof(*object)); + if (!object) + return E_OUTOFMEMORY; + + object->IMFAsyncCallback_iface.lpVtbl = &periodic_callback_vtbl; + object->refcount = 1; + object->callback = callback; + + *out = &object->IMFAsyncCallback_iface; + + return S_OK; +} + +/*********************************************************************** + * MFAddPeriodicCallback (mfplat.@) + */ +HRESULT WINAPI MFAddPeriodicCallback(MFPERIODICCALLBACK callback, IUnknown *context, DWORD *key) +{ + IMFAsyncCallback *periodic_callback; + MFWORKITEM_KEY workitem_key; + IMFAsyncResult *result; + struct queue *queue; + HRESULT hr; + + TRACE("%p, %p, %p.\n", callback, context, key); + + if (FAILED(hr = grab_queue(MFASYNC_CALLBACK_QUEUE_TIMER, &queue))) + return hr; + + if (FAILED(hr = create_periodic_callback_obj(callback, &periodic_callback))) + return hr; + + hr = create_async_result(context, periodic_callback, NULL, &result); + IMFAsyncCallback_Release(periodic_callback); + if (FAILED(hr)) + return hr; + + hr = queue_submit_timer(queue, result, 0, get_timer_period(), key ? &workitem_key : NULL); + + IMFAsyncResult_Release(result); + + if (key) + *key = workitem_key; + + return S_OK; +} + +/*********************************************************************** + * MFRemovePeriodicCallback (mfplat.@) + */ +HRESULT WINAPI MFRemovePeriodicCallback(DWORD key) +{ + struct queue *queue; + HRESULT hr; + + TRACE("%#x.\n", key); + + if (FAILED(hr = grab_queue(MFASYNC_CALLBACK_QUEUE_TIMER, &queue))) + return hr; + + return queue_cancel_item(queue, get_item_key(SCHEDULED_ITEM_KEY_MASK, key)); +} diff --git a/dlls/mfplat/tests/mfplat.c b/dlls/mfplat/tests/mfplat.c index 2a2189b4d3..e413d61dfa 100644 --- a/dlls/mfplat/tests/mfplat.c +++ b/dlls/mfplat/tests/mfplat.c @@ -45,6 +45,8 @@ static void* (WINAPI *pMFHeapAlloc)(SIZE_T size, ULONG flags, char *file, int static void (WINAPI *pMFHeapFree)(void *p); static HRESULT (WINAPI *pMFPutWaitingWorkItem)(HANDLE event, LONG priority, IMFAsyncResult *result, MFWORKITEM_KEY *key); static HRESULT (WINAPI *pMFAllocateSerialWorkQueue)(DWORD queue, DWORD *serial_queue); +static HRESULT (WINAPI *pMFAddPeriodicCallback)(MFPERIODICCALLBACK callback, IUnknown *context, DWORD *key); +static HRESULT (WINAPI *pMFRemovePeriodicCallback)(DWORD key);
DEFINE_GUID(GUID_NULL,0,0,0,0,0,0,0,0,0,0,0);
@@ -314,6 +316,7 @@ static void init_functions(void) HMODULE mod = GetModuleHandleA("mfplat.dll");
#define X(f) p##f = (void*)GetProcAddress(mod, #f) + X(MFAddPeriodicCallback); X(MFAllocateSerialWorkQueue); X(MFCopyImage); X(MFCreateSourceResolver); @@ -322,6 +325,7 @@ static void init_functions(void) X(MFHeapAlloc); X(MFHeapFree); X(MFPutWaitingWorkItem); + X(MFRemovePeriodicCallback); #undef X }
@@ -1199,6 +1203,49 @@ static void test_serial_queue(void) ok(hr == S_OK, "Failed to shut down, hr %#x.\n", hr); }
+static LONG periodic_counter; +static void CALLBACK periodic_callback(IUnknown *context) +{ + InterlockedIncrement(&periodic_counter); +} + +static void test_periodic_callback(void) +{ + DWORD period, key; + HRESULT hr; + + hr = MFStartup(MF_VERSION, MFSTARTUP_FULL); + ok(hr == S_OK, "Failed to start up, hr %#x.\n", hr); + + period = 0; + hr = MFGetTimerPeriodicity(&period); + ok(hr == S_OK, "Failed to get timer perdiod, hr %#x.\n", hr); + ok(period == 10, "Unexpected period %u.\n", period); + + if (!pMFAddPeriodicCallback) + { + win_skip("Periodic callbacks are not supported.\n"); + MFShutdown(); + return; + } + + ok(periodic_counter == 0, "Unexpected counter value %u.\n", periodic_counter); + + hr = pMFAddPeriodicCallback(periodic_callback, NULL, &key); + ok(hr == S_OK, "Failed to add periodic callback, hr %#x.\n", hr); + ok(key != 0, "Unexpected key %#x.\n", key); + + Sleep(10 * period); + + hr = pMFRemovePeriodicCallback(key); + ok(hr == S_OK, "Failed to remove callback, hr %#x.\n", hr); + + ok(periodic_counter > 0, "Unexpected counter value %u.\n", periodic_counter); + + hr = MFShutdown(); + ok(hr == S_OK, "Failed to shut down, hr %#x.\n", hr); +} + START_TEST(mfplat) { CoInitialize(NULL); @@ -1222,6 +1269,7 @@ START_TEST(mfplat) test_MFHeapAlloc(); test_scheduled_items(); test_serial_queue(); + test_periodic_callback();
CoUninitialize(); } diff --git a/include/mfapi.h b/include/mfapi.h index ee5cf4c069..129a19c883 100644 --- a/include/mfapi.h +++ b/include/mfapi.h @@ -95,6 +95,9 @@ typedef enum MF_MULTITHREADED_WORKQUEUE, } MFASYNC_WORKQUEUE_TYPE;
+typedef void (*CALLBACK MFPERIODICCALLBACK)(IUnknown *context); + +HRESULT WINAPI MFAddPeriodicCallback(MFPERIODICCALLBACK callback, IUnknown *context, DWORD *key); HRESULT WINAPI MFAllocateWorkQueue(DWORD *queue); HRESULT WINAPI MFAllocateWorkQueueEx(MFASYNC_WORKQUEUE_TYPE queue_type, DWORD *queue); HRESULT WINAPI MFCancelWorkItem(MFWORKITEM_KEY key); @@ -131,6 +134,7 @@ HRESULT WINAPI MFTRegister(CLSID clsid, GUID category, LPWSTR name, UINT32 flags HRESULT WINAPI MFTRegisterLocal(IClassFactory *factory, REFGUID category, LPCWSTR name, UINT32 flags, UINT32 cinput, const MFT_REGISTER_TYPE_INFO *input_types, UINT32 coutput, const MFT_REGISTER_TYPE_INFO* output_types); +HRESULT WINAPI MFRemovePeriodicCallback(DWORD key); HRESULT WINAPI MFShutdown(void); HRESULT WINAPI MFStartup(ULONG version, DWORD flags); HRESULT WINAPI MFUnlockPlatform(void);