[PATCH 1/9] mfplat: Implement periodic callbacks.

Nikolay Sivov nsivov at codeweavers.com
Mon Mar 4 03:27:18 CST 2019


Signed-off-by: Nikolay Sivov <nsivov at codeweavers.com>
---
 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);
-- 
2.20.1




More information about the wine-devel mailing list