[PATCH 3/9] mfplat: Implement media event queue.

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


Signed-off-by: Nikolay Sivov <nsivov at codeweavers.com>
---
 dlls/mfplat/main.c         | 336 ++++++++++++++++++++++++++++---------
 dlls/mfplat/tests/mfplat.c | 202 +++++++++++++++++++++-
 include/mferror.h          |   1 +
 include/mfobjects.idl      |   2 +
 4 files changed, 455 insertions(+), 86 deletions(-)

diff --git a/dlls/mfplat/main.c b/dlls/mfplat/main.c
index 0bf7cbf4c9..0ffcd88a38 100644
--- a/dlls/mfplat/main.c
+++ b/dlls/mfplat/main.c
@@ -35,6 +35,7 @@
 #include "wine/heap.h"
 #include "wine/debug.h"
 #include "wine/unicode.h"
+#include "wine/list.h"
 
 #include "mfplat_private.h"
 
@@ -2797,6 +2798,9 @@ static const IMFMediaEventVtbl mfmediaevent_vtbl =
     mfmediaevent_GetValue,
 };
 
+/***********************************************************************
+ *      MFCreateMediaEvent (mfplat.@)
+ */
 HRESULT WINAPI MFCreateMediaEvent(MediaEventType type, REFGUID extended_type, HRESULT status,
                                   const PROPVARIANT *value, IMFMediaEvent **event)
 {
@@ -2824,141 +2828,316 @@ HRESULT WINAPI MFCreateMediaEvent(MediaEventType type, REFGUID extended_type, HR
     return S_OK;
 }
 
-typedef struct _mfeventqueue
+struct event_queue
 {
     IMFMediaEventQueue IMFMediaEventQueue_iface;
-    LONG ref;
-} mfeventqueue;
+    LONG refcount;
+
+    CRITICAL_SECTION cs;
+    CONDITION_VARIABLE update_event;
+    struct list events;
+    BOOL is_shut_down;
+    IMFAsyncResult *subscriber;
+};
+
+struct queued_event
+{
+    struct list entry;
+    IMFMediaEvent *event;
+};
 
-static inline mfeventqueue *impl_from_IMFMediaEventQueue(IMFMediaEventQueue *iface)
+static inline struct event_queue *impl_from_IMFMediaEventQueue(IMFMediaEventQueue *iface)
 {
-    return CONTAINING_RECORD(iface, mfeventqueue, IMFMediaEventQueue_iface);
+    return CONTAINING_RECORD(iface, struct event_queue, IMFMediaEventQueue_iface);
 }
 
-static HRESULT WINAPI mfeventqueue_QueryInterface(IMFMediaEventQueue *iface, REFIID riid, void **out)
+static IMFMediaEvent *queue_pop_event(struct event_queue *queue)
 {
-    mfeventqueue *This = impl_from_IMFMediaEventQueue(iface);
+    struct list *head = list_head(&queue->events);
+    struct queued_event *queued_event;
+    IMFMediaEvent *event;
 
-    TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), out);
+    if (!head)
+        return NULL;
 
-    if(IsEqualGUID(riid, &IID_IUnknown)      ||
-       IsEqualGUID(riid, &IID_IMFMediaEventQueue))
-    {
-        *out = &This->IMFMediaEventQueue_iface;
-    }
-    else
+    queued_event = LIST_ENTRY(head, struct queued_event, entry);
+    event = queued_event->event;
+    list_remove(&queued_event->entry);
+    heap_free(queued_event);
+    return event;
+}
+
+static void event_queue_cleanup(struct event_queue *queue)
+{
+    IMFMediaEvent *event;
+
+    while ((event = queue_pop_event(queue)))
+        IMFMediaEvent_Release(event);
+}
+
+static HRESULT WINAPI eventqueue_QueryInterface(IMFMediaEventQueue *iface, REFIID riid, void **out)
+{
+    struct event_queue *queue = impl_from_IMFMediaEventQueue(iface);
+
+    TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), out);
+
+    if (IsEqualIID(riid, &IID_IMFMediaEventQueue) ||
+            IsEqualIID(riid, &IID_IUnknown))
     {
-        FIXME("(%s, %p)\n", debugstr_guid(riid), out);
-        *out = NULL;
-        return E_NOINTERFACE;
+        *out = &queue->IMFMediaEventQueue_iface;
+        IMFMediaEventQueue_AddRef(iface);
+        return S_OK;
     }
 
-    IUnknown_AddRef((IUnknown*)*out);
-    return S_OK;
+    WARN("Unsupported %s.\n", debugstr_guid(riid));
+    *out = NULL;
+    return E_NOINTERFACE;
 }
 
-static ULONG WINAPI mfeventqueue_AddRef(IMFMediaEventQueue *iface)
+static ULONG WINAPI eventqueue_AddRef(IMFMediaEventQueue *iface)
 {
-    mfeventqueue *This = impl_from_IMFMediaEventQueue(iface);
-    ULONG ref = InterlockedIncrement(&This->ref);
+    struct event_queue *queue = impl_from_IMFMediaEventQueue(iface);
+    ULONG refcount = InterlockedIncrement(&queue->refcount);
 
-    TRACE("(%p) ref=%u\n", This, ref);
+    TRACE("%p, refcount %u.\n", iface, refcount);
 
-    return ref;
+    return refcount;
 }
 
-static ULONG WINAPI mfeventqueue_Release(IMFMediaEventQueue *iface)
+static ULONG WINAPI eventqueue_Release(IMFMediaEventQueue *iface)
 {
-    mfeventqueue *This = impl_from_IMFMediaEventQueue(iface);
-    ULONG ref = InterlockedDecrement(&This->ref);
+    struct event_queue *queue = impl_from_IMFMediaEventQueue(iface);
+    ULONG refcount = InterlockedDecrement(&queue->refcount);
 
-    TRACE("(%p) ref=%u\n", This, ref);
+    TRACE("%p, refcount %u.\n", queue, refcount);
 
-    if (!ref)
+    if (!refcount)
     {
-        HeapFree(GetProcessHeap(), 0, This);
+        event_queue_cleanup(queue);
+        DeleteCriticalSection(&queue->cs);
+        heap_free(queue);
     }
 
-    return ref;
+    return refcount;
 }
 
-static HRESULT WINAPI mfeventqueue_GetEvent(IMFMediaEventQueue *iface, DWORD flags, IMFMediaEvent **event)
+static HRESULT WINAPI eventqueue_GetEvent(IMFMediaEventQueue *iface, DWORD flags, IMFMediaEvent **event)
 {
-    mfeventqueue *This = impl_from_IMFMediaEventQueue(iface);
+    struct event_queue *queue = impl_from_IMFMediaEventQueue(iface);
+    HRESULT hr = S_OK;
 
-    FIXME("%p, %p\n", This, event);
+    TRACE("%p, %p.\n", iface, event);
 
-    return E_NOTIMPL;
+    EnterCriticalSection(&queue->cs);
+
+    if (queue->is_shut_down)
+        hr = MF_E_SHUTDOWN;
+    else if (queue->subscriber)
+        hr = MF_E_MULTIPLE_SUBSCRIBERS;
+    else
+    {
+        if (flags & MF_EVENT_FLAG_NO_WAIT)
+        {
+            if (!(*event = queue_pop_event(queue)))
+                hr = MF_E_NO_EVENTS_AVAILABLE;
+        }
+        else
+        {
+            while (list_empty(&queue->events) && !queue->is_shut_down)
+            {
+                SleepConditionVariableCS(&queue->update_event, &queue->cs, INFINITE);
+            }
+            *event = queue_pop_event(queue);
+            if (queue->is_shut_down)
+                hr = MF_E_SHUTDOWN;
+        }
+    }
+
+    LeaveCriticalSection(&queue->cs);
+
+    return hr;
 }
 
-static HRESULT WINAPI mfeventqueue_BeginGetEvent(IMFMediaEventQueue *iface, IMFAsyncCallback *callback, IUnknown *state)
+static void queue_notify_subscriber(struct event_queue *queue)
 {
-    mfeventqueue *This = impl_from_IMFMediaEventQueue(iface);
+    if (list_empty(&queue->events) || !queue->subscriber)
+        return;
 
-    FIXME("%p, %p, %p\n", This, callback, state);
+    MFPutWorkItemEx(MFASYNC_CALLBACK_QUEUE_STANDARD, queue->subscriber);
+}
 
-    return E_NOTIMPL;
+static HRESULT WINAPI eventqueue_BeginGetEvent(IMFMediaEventQueue *iface, IMFAsyncCallback *callback, IUnknown *state)
+{
+    struct event_queue *queue = impl_from_IMFMediaEventQueue(iface);
+    MFASYNCRESULT *result_data = (MFASYNCRESULT *)queue->subscriber;
+    HRESULT hr;
+
+    TRACE("%p, %p, %p.\n", iface, callback, state);
+
+    if (!callback)
+        return E_INVALIDARG;
+
+    EnterCriticalSection(&queue->cs);
+
+    if (queue->is_shut_down)
+        hr = MF_E_SHUTDOWN;
+    else if (result_data)
+    {
+        if (result_data->pCallback == callback)
+            hr = IMFAsyncResult_GetStateNoAddRef(queue->subscriber) == state ?
+                    MF_S_MULTIPLE_BEGIN : MF_E_MULTIPLE_BEGIN;
+        else
+            hr = MF_E_MULTIPLE_SUBSCRIBERS;
+    }
+    else
+    {
+        hr = MFCreateAsyncResult(NULL, callback, state, &queue->subscriber);
+        if (SUCCEEDED(hr))
+            queue_notify_subscriber(queue);
+    }
+
+    LeaveCriticalSection(&queue->cs);
+
+    return hr;
 }
 
-static HRESULT WINAPI mfeventqueue_EndGetEvent(IMFMediaEventQueue *iface, IMFAsyncResult *result, IMFMediaEvent **event)
+static HRESULT WINAPI eventqueue_EndGetEvent(IMFMediaEventQueue *iface, IMFAsyncResult *result, IMFMediaEvent **event)
 {
-    mfeventqueue *This = impl_from_IMFMediaEventQueue(iface);
+    struct event_queue *queue = impl_from_IMFMediaEventQueue(iface);
+    HRESULT hr = E_FAIL;
 
-    FIXME("%p, %p, %p\n", This, result, event);
+    TRACE("%p, %p, %p.\n", iface, result, event);
 
-    return E_NOTIMPL;
+    EnterCriticalSection(&queue->cs);
+
+    if (queue->is_shut_down)
+        hr = MF_E_SHUTDOWN;
+    else if (queue->subscriber == result)
+    {
+        *event = queue_pop_event(queue);
+        if (queue->subscriber)
+            IMFAsyncResult_Release(queue->subscriber);
+        queue->subscriber = NULL;
+        hr = *event ? S_OK : E_FAIL;
+    }
+
+    LeaveCriticalSection(&queue->cs);
+
+    return hr;
 }
 
-static HRESULT WINAPI mfeventqueue_QueueEvent(IMFMediaEventQueue *iface, IMFMediaEvent *event)
+static HRESULT eventqueue_queue_event(struct event_queue *queue, IMFMediaEvent *event)
 {
-    mfeventqueue *This = impl_from_IMFMediaEventQueue(iface);
+    struct queued_event *queued_event;
+    HRESULT hr = S_OK;
+
+    queued_event = heap_alloc(sizeof(*queued_event));
+    if (!queued_event)
+        return E_OUTOFMEMORY;
 
-    FIXME("%p, %p\n", This, event);
+    queued_event->event = event;
 
-    return E_NOTIMPL;
+    EnterCriticalSection(&queue->cs);
+
+    if (queue->is_shut_down)
+        hr = MF_E_SHUTDOWN;
+    else
+    {
+        IMFMediaEvent_AddRef(queued_event->event);
+        list_add_tail(&queue->events, &queued_event->entry);
+        queue_notify_subscriber(queue);
+    }
+
+    LeaveCriticalSection(&queue->cs);
+
+    if (FAILED(hr))
+        heap_free(queued_event);
+
+    WakeAllConditionVariable(&queue->update_event);
+
+    return hr;
 }
 
-static HRESULT WINAPI mfeventqueue_QueueEventParamVar(IMFMediaEventQueue *iface, MediaEventType met,
-        REFGUID type, HRESULT status, const PROPVARIANT *value)
+static HRESULT WINAPI eventqueue_QueueEvent(IMFMediaEventQueue *iface, IMFMediaEvent *event)
 {
-    mfeventqueue *This = impl_from_IMFMediaEventQueue(iface);
+    struct event_queue *queue = impl_from_IMFMediaEventQueue(iface);
 
-    FIXME("%p, %d, %s, 0x%08x, %p\n", This, met, debugstr_guid(type), status, value);
+    TRACE("%p, %p.\n", iface, event);
 
-    return E_NOTIMPL;
+    return eventqueue_queue_event(queue, event);
 }
 
-static HRESULT WINAPI mfeventqueue_QueueEventParamUnk(IMFMediaEventQueue *iface, MediaEventType met, REFGUID type,
-        HRESULT status, IUnknown *unk)
+static HRESULT WINAPI eventqueue_QueueEventParamVar(IMFMediaEventQueue *iface, MediaEventType event_type,
+        REFGUID extended_type, HRESULT status, const PROPVARIANT *value)
 {
-    mfeventqueue *This = impl_from_IMFMediaEventQueue(iface);
+    struct event_queue *queue = impl_from_IMFMediaEventQueue(iface);
+    IMFMediaEvent *event;
+    HRESULT hr;
 
-    FIXME("%p, %d, %s, 0x%08x, %p\n", This, met, debugstr_guid(type), status, unk);
+    TRACE("%p, %d, %s, %#x, %p\n", iface, event_type, debugstr_guid(extended_type), status, value);
 
-    return E_NOTIMPL;
+    if (FAILED(hr = MFCreateMediaEvent(event_type, extended_type, status, value, &event)))
+        return hr;
+
+    hr = eventqueue_queue_event(queue, event);
+    IMFMediaEvent_Release(event);
+    return hr;
 }
 
-static HRESULT WINAPI mfeventqueue_Shutdown(IMFMediaEventQueue *iface)
+static HRESULT WINAPI eventqueue_QueueEventParamUnk(IMFMediaEventQueue *iface, MediaEventType event_type,
+        REFGUID extended_type, HRESULT status, IUnknown *unk)
 {
-    mfeventqueue *This = impl_from_IMFMediaEventQueue(iface);
+    struct event_queue *queue = impl_from_IMFMediaEventQueue(iface);
+    IMFMediaEvent *event;
+    PROPVARIANT value;
+    HRESULT hr;
 
-    FIXME("%p\n", This);
+    TRACE("%p, %d, %s, %#x, %p.\n", iface, event_type, debugstr_guid(extended_type), status, unk);
 
-    return E_NOTIMPL;
+    value.vt = VT_UNKNOWN;
+    value.punkVal = unk;
+
+    if (FAILED(hr = MFCreateMediaEvent(event_type, extended_type, status, &value, &event)))
+        return hr;
+
+    hr = eventqueue_queue_event(queue, event);
+    IMFMediaEvent_Release(event);
+    return hr;
 }
 
-static const IMFMediaEventQueueVtbl mfeventqueue_vtbl =
+static HRESULT WINAPI eventqueue_Shutdown(IMFMediaEventQueue *iface)
 {
-    mfeventqueue_QueryInterface,
-    mfeventqueue_AddRef,
-    mfeventqueue_Release,
-    mfeventqueue_GetEvent,
-    mfeventqueue_BeginGetEvent,
-    mfeventqueue_EndGetEvent,
-    mfeventqueue_QueueEvent,
-    mfeventqueue_QueueEventParamVar,
-    mfeventqueue_QueueEventParamUnk,
-    mfeventqueue_Shutdown
+    struct event_queue *queue = impl_from_IMFMediaEventQueue(iface);
+
+    TRACE("%p\n", queue);
+
+    EnterCriticalSection(&queue->cs);
+
+    if (!queue->is_shut_down)
+    {
+        event_queue_cleanup(queue);
+        queue->is_shut_down = TRUE;
+    }
+
+    LeaveCriticalSection(&queue->cs);
+
+    WakeAllConditionVariable(&queue->update_event);
+
+    return S_OK;
+}
+
+static const IMFMediaEventQueueVtbl eventqueuevtbl =
+{
+    eventqueue_QueryInterface,
+    eventqueue_AddRef,
+    eventqueue_Release,
+    eventqueue_GetEvent,
+    eventqueue_BeginGetEvent,
+    eventqueue_EndGetEvent,
+    eventqueue_QueueEvent,
+    eventqueue_QueueEventParamVar,
+    eventqueue_QueueEventParamUnk,
+    eventqueue_Shutdown
 };
 
 /***********************************************************************
@@ -2966,16 +3145,19 @@ static const IMFMediaEventQueueVtbl mfeventqueue_vtbl =
  */
 HRESULT WINAPI MFCreateEventQueue(IMFMediaEventQueue **queue)
 {
-    mfeventqueue *object;
+    struct event_queue *object;
 
     TRACE("%p\n", queue);
 
-    object = HeapAlloc( GetProcessHeap(), 0, sizeof(*object) );
-    if(!object)
+    object = heap_alloc_zero(sizeof(*object));
+    if (!object)
         return E_OUTOFMEMORY;
 
-    object->ref = 1;
-    object->IMFMediaEventQueue_iface.lpVtbl = &mfeventqueue_vtbl;
+    object->IMFMediaEventQueue_iface.lpVtbl = &eventqueuevtbl;
+    object->refcount = 1;
+    list_init(&object->events);
+    InitializeCriticalSection(&object->cs);
+    InitializeConditionVariable(&object->update_event);
 
     *queue = &object->IMFMediaEventQueue_iface;
 
diff --git a/dlls/mfplat/tests/mfplat.c b/dlls/mfplat/tests/mfplat.c
index e413d61dfa..4405bd9282 100644
--- a/dlls/mfplat/tests/mfplat.c
+++ b/dlls/mfplat/tests/mfplat.c
@@ -36,6 +36,8 @@
 
 #include "wine/test.h"
 
+static BOOL is_win8_plus;
+
 static HRESULT (WINAPI *pMFCopyImage)(BYTE *dest, LONG deststride, const BYTE *src, LONG srcstride,
         DWORD width, DWORD lines);
 static HRESULT (WINAPI *pMFCreateSourceResolver)(IMFSourceResolver **resolver);
@@ -327,6 +329,8 @@ static void init_functions(void)
     X(MFPutWaitingWorkItem);
     X(MFRemovePeriodicCallback);
 #undef X
+
+    is_win8_plus = pMFPutWaitingWorkItem != NULL;
 }
 
 static void test_MFCreateMediaType(void)
@@ -700,6 +704,17 @@ static void test_MFSample(void)
     IMFSample_Release(sample);
 }
 
+struct test_callback
+{
+    IMFAsyncCallback IMFAsyncCallback_iface;
+    HANDLE event;
+};
+
+static struct test_callback *impl_from_IMFAsyncCallback(IMFAsyncCallback *iface)
+{
+    return CONTAINING_RECORD(iface, struct test_callback, IMFAsyncCallback_iface);
+}
+
 static HRESULT WINAPI testcallback_QueryInterface(IMFAsyncCallback *iface, REFIID riid, void **obj)
 {
     if (IsEqualIID(riid, &IID_IMFAsyncCallback) ||
@@ -732,7 +747,43 @@ static HRESULT WINAPI testcallback_GetParameters(IMFAsyncCallback *iface, DWORD
 
 static HRESULT WINAPI testcallback_Invoke(IMFAsyncCallback *iface, IMFAsyncResult *result)
 {
+    struct test_callback *callback = impl_from_IMFAsyncCallback(iface);
+    IMFMediaEventQueue *queue;
+    IUnknown *state, *obj;
+    HRESULT hr;
+
     ok(result != NULL, "Unexpected result object.\n");
+
+    state = IMFAsyncResult_GetStateNoAddRef(result);
+    if (state && SUCCEEDED(IUnknown_QueryInterface(state, &IID_IMFMediaEventQueue, (void **)&queue)))
+    {
+        IMFMediaEvent *event;
+
+        if (is_win8_plus)
+        {
+            hr = IMFMediaEventQueue_GetEvent(queue, MF_EVENT_FLAG_NO_WAIT, &event);
+            ok(hr == MF_E_MULTIPLE_SUBSCRIBERS, "Failed to get event, hr %#x.\n", hr);
+
+            hr = IMFMediaEventQueue_GetEvent(queue, 0, &event);
+            ok(hr == MF_E_MULTIPLE_SUBSCRIBERS, "Failed to get event, hr %#x.\n", hr);
+        }
+
+        hr = IMFMediaEventQueue_EndGetEvent(queue, result, &event);
+        ok(hr == S_OK, "Failed to finalize GetEvent, hr %#x.\n", hr);
+
+        hr = IMFAsyncResult_GetObject(result, &obj);
+        ok(hr == E_POINTER, "Unexpected hr %#x.\n", hr);
+
+        IMFMediaEvent_Release(event);
+
+        hr = IMFMediaEventQueue_EndGetEvent(queue, result, &event);
+        ok(hr == E_FAIL, "Unexpected result, hr %#x.\n", hr);
+
+        IMFMediaEventQueue_Release(queue);
+
+        SetEvent(callback->event);
+    }
+
     return E_NOTIMPL;
 }
 
@@ -745,15 +796,23 @@ static const IMFAsyncCallbackVtbl testcallbackvtbl =
     testcallback_Invoke,
 };
 
+static void init_test_callback(struct test_callback *callback)
+{
+    callback->IMFAsyncCallback_iface.lpVtbl = &testcallbackvtbl;
+    callback->event = NULL;
+}
+
 static void test_MFCreateAsyncResult(void)
 {
-    IMFAsyncCallback callback = { &testcallbackvtbl };
     IMFAsyncResult *result, *result2;
+    struct test_callback callback;
     IUnknown *state, *object;
     MFASYNCRESULT *data;
     ULONG refcount;
     HRESULT hr;
 
+    init_test_callback(&callback);
+
     hr = MFCreateAsyncResult(NULL, NULL, NULL, NULL);
     ok(FAILED(hr), "Unexpected hr %#x.\n", hr);
 
@@ -797,11 +856,11 @@ static void test_MFCreateAsyncResult(void)
     ok(state == NULL, "Unexpected state.\n");
 
     /* Object. */
-    hr = MFCreateAsyncResult((IUnknown *)result, &callback, NULL, &result2);
+    hr = MFCreateAsyncResult((IUnknown *)result, &callback.IMFAsyncCallback_iface, NULL, &result2);
     ok(hr == S_OK, "Failed to create object, hr %#x.\n", hr);
 
     data = (MFASYNCRESULT *)result2;
-    ok(data->pCallback == &callback, "Unexpected callback value.\n");
+    ok(data->pCallback == &callback.IMFAsyncCallback_iface, "Unexpected callback value.\n");
     ok(data->hrStatusResult == S_OK, "Unexpected status %#x.\n", data->hrStatusResult);
     ok(data->dwBytesTransferred == 0, "Unexpected byte length %u.\n", data->dwBytesTransferred);
     ok(data->hEvent == NULL, "Unexpected event.\n");
@@ -815,11 +874,11 @@ static void test_MFCreateAsyncResult(void)
     IMFAsyncResult_Release(result2);
 
     /* State object. */
-    hr = MFCreateAsyncResult(NULL, &callback, (IUnknown *)result, &result2);
+    hr = MFCreateAsyncResult(NULL, &callback.IMFAsyncCallback_iface, (IUnknown *)result, &result2);
     ok(hr == S_OK, "Failed to create object, hr %#x.\n", hr);
 
     data = (MFASYNCRESULT *)result2;
-    ok(data->pCallback == &callback, "Unexpected callback value.\n");
+    ok(data->pCallback == &callback.IMFAsyncCallback_iface, "Unexpected callback value.\n");
     ok(data->hrStatusResult == S_OK, "Unexpected status %#x.\n", data->hrStatusResult);
     ok(data->dwBytesTransferred == 0, "Unexpected byte length %u.\n", data->dwBytesTransferred);
     ok(data->hEvent == NULL, "Unexpected event.\n");
@@ -1096,15 +1155,17 @@ static void test_MFHeapAlloc(void)
 
 static void test_scheduled_items(void)
 {
-    IMFAsyncCallback callback = { &testcallbackvtbl };
+    struct test_callback callback;
     IMFAsyncResult *result;
     MFWORKITEM_KEY key, key2;
     HRESULT hr;
 
+    init_test_callback(&callback);
+
     hr = MFStartup(MF_VERSION, MFSTARTUP_FULL);
     ok(hr == S_OK, "Failed to start up, hr %#x.\n", hr);
 
-    hr = MFScheduleWorkItem(&callback, NULL, -5000, &key);
+    hr = MFScheduleWorkItem(&callback.IMFAsyncCallback_iface, NULL, -5000, &key);
     ok(hr == S_OK, "Failed to schedule item, hr %#x.\n", hr);
 
     hr = MFCancelWorkItem(key);
@@ -1119,7 +1180,7 @@ static void test_scheduled_items(void)
         return;
     }
 
-    hr = MFCreateAsyncResult(NULL, &callback, NULL, &result);
+    hr = MFCreateAsyncResult(NULL, &callback.IMFAsyncCallback_iface, NULL, &result);
     ok(hr == S_OK, "Failed to create result, hr %#x.\n", hr);
 
     hr = pMFPutWaitingWorkItem(NULL, 0, result, &key);
@@ -1136,7 +1197,7 @@ static void test_scheduled_items(void)
 
     IMFAsyncResult_Release(result);
 
-    hr = MFScheduleWorkItem(&callback, NULL, -5000, &key);
+    hr = MFScheduleWorkItem(&callback.IMFAsyncCallback_iface, NULL, -5000, &key);
     ok(hr == S_OK, "Failed to schedule item, hr %#x.\n", hr);
 
     hr = MFCancelWorkItem(key);
@@ -1246,6 +1307,128 @@ static void test_periodic_callback(void)
     ok(hr == S_OK, "Failed to shut down, hr %#x.\n", hr);
 }
 
+static void test_event_queue(void)
+{
+    struct test_callback callback, callback2;
+    IMFMediaEvent *event, *event2;
+    IMFMediaEventQueue *queue;
+    IMFAsyncResult *result;
+    HRESULT hr;
+    DWORD ret;
+
+    init_test_callback(&callback);
+    init_test_callback(&callback2);
+
+    hr = MFStartup(MF_VERSION, MFSTARTUP_FULL);
+    ok(hr == S_OK, "Failed to start up, hr %#x.\n", hr);
+
+    hr = MFCreateEventQueue(&queue);
+    ok(hr == S_OK, "Failed to create event queue, hr %#x.\n", hr);
+
+    hr = IMFMediaEventQueue_GetEvent(queue, MF_EVENT_FLAG_NO_WAIT, &event);
+    ok(hr == MF_E_NO_EVENTS_AVAILABLE, "Unexpected hr %#x.\n", hr);
+
+    hr = MFCreateMediaEvent(MEError, &GUID_NULL, E_FAIL, NULL, &event);
+    ok(hr == S_OK, "Failed to create event object, hr %#x.\n", hr);
+
+    if (is_win8_plus)
+    {
+        hr = IMFMediaEventQueue_QueueEvent(queue, event);
+        ok(hr == S_OK, "Failed to queue event, hr %#x.\n", hr);
+
+        hr = IMFMediaEventQueue_GetEvent(queue, MF_EVENT_FLAG_NO_WAIT, &event2);
+        ok(hr == S_OK, "Unexpected hr %#x.\n", hr);
+        ok(event2 == event, "Unexpected event object.\n");
+        IMFMediaEvent_Release(event2);
+
+        hr = IMFMediaEventQueue_QueueEvent(queue, event);
+        ok(hr == S_OK, "Failed to queue event, hr %#x.\n", hr);
+
+        hr = IMFMediaEventQueue_GetEvent(queue, 0, &event2);
+        ok(hr == S_OK, "Unexpected hr %#x.\n", hr);
+        IMFMediaEvent_Release(event2);
+    }
+
+    /* Async case. */
+    hr = IMFMediaEventQueue_BeginGetEvent(queue, NULL, NULL);
+    ok(hr == E_INVALIDARG, "Unexpected hr %#x.\n", hr);
+
+    hr = IMFMediaEventQueue_BeginGetEvent(queue, &callback.IMFAsyncCallback_iface, (IUnknown *)queue);
+    ok(hr == S_OK, "Failed to Begin*, hr %#x.\n", hr);
+
+    /* Same callback, same state. */
+    hr = IMFMediaEventQueue_BeginGetEvent(queue, &callback.IMFAsyncCallback_iface, (IUnknown *)queue);
+    ok(hr == MF_S_MULTIPLE_BEGIN, "Unexpected hr %#x.\n", hr);
+
+    /* Same callback, different state. */
+    hr = IMFMediaEventQueue_BeginGetEvent(queue, &callback.IMFAsyncCallback_iface, (IUnknown *)&callback);
+    ok(hr == MF_E_MULTIPLE_BEGIN, "Unexpected hr %#x.\n", hr);
+
+    /* Different callback, same state. */
+    hr = IMFMediaEventQueue_BeginGetEvent(queue, &callback2.IMFAsyncCallback_iface, (IUnknown *)queue);
+    ok(hr == MF_E_MULTIPLE_SUBSCRIBERS, "Unexpected hr %#x.\n", hr);
+
+    /* Different callback, different state. */
+    hr = IMFMediaEventQueue_BeginGetEvent(queue, &callback2.IMFAsyncCallback_iface, (IUnknown *)&callback.IMFAsyncCallback_iface);
+    ok(hr == MF_E_MULTIPLE_SUBSCRIBERS, "Unexpected hr %#x.\n", hr);
+
+    callback.event = CreateEventA(NULL, FALSE, FALSE, NULL);
+
+    hr = IMFMediaEventQueue_QueueEvent(queue, event);
+    ok(hr == S_OK, "Failed to queue event, hr %#x.\n", hr);
+
+    ret = WaitForSingleObject(callback.event, 100);
+    ok(ret == WAIT_OBJECT_0, "Unexpected return value %#x.\n", ret);
+
+    CloseHandle(callback.event);
+
+    IMFMediaEvent_Release(event);
+
+    hr = MFCreateAsyncResult(NULL, &callback.IMFAsyncCallback_iface, NULL, &result);
+    ok(hr == S_OK, "Failed to create result, hr %#x.\n", hr);
+
+    hr = IMFMediaEventQueue_EndGetEvent(queue, result, &event);
+    ok(hr == E_FAIL, "Unexpected hr %#x.\n", hr);
+
+    /* Shutdown behavior. */
+    hr = IMFMediaEventQueue_Shutdown(queue);
+    ok(hr == S_OK, "Failed to shut down, hr %#x.\n", hr);
+
+    hr = IMFMediaEventQueue_GetEvent(queue, MF_EVENT_FLAG_NO_WAIT, &event);
+    ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#x.\n", hr);
+
+    hr = MFCreateMediaEvent(MEError, &GUID_NULL, E_FAIL, NULL, &event);
+    ok(hr == S_OK, "Failed to create event object, hr %#x.\n", hr);
+    hr = IMFMediaEventQueue_QueueEvent(queue, event);
+    ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#x.\n", hr);
+    IMFMediaEvent_Release(event);
+
+    hr = IMFMediaEventQueue_QueueEventParamUnk(queue, MEError, &GUID_NULL, E_FAIL, NULL);
+    ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#x.\n", hr);
+
+    hr = IMFMediaEventQueue_QueueEventParamVar(queue, MEError, &GUID_NULL, E_FAIL, NULL);
+    ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#x.\n", hr);
+
+    hr = IMFMediaEventQueue_BeginGetEvent(queue, &callback.IMFAsyncCallback_iface, NULL);
+    ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#x.\n", hr);
+
+    hr = IMFMediaEventQueue_BeginGetEvent(queue, NULL, NULL);
+    ok(hr == E_INVALIDARG, "Unexpected hr %#x.\n", hr);
+
+    hr = IMFMediaEventQueue_EndGetEvent(queue, result, &event);
+    ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#x.\n", hr);
+    IMFAsyncResult_Release(result);
+
+    /* Already shut down. */
+    hr = IMFMediaEventQueue_Shutdown(queue);
+    ok(hr == S_OK, "Failed to shut down, hr %#x.\n", hr);
+
+    IMFMediaEventQueue_Release(queue);
+
+    hr = MFShutdown();
+    ok(hr == S_OK, "Failed to shut down, hr %#x.\n", hr);
+}
+
 START_TEST(mfplat)
 {
     CoInitialize(NULL);
@@ -1270,6 +1453,7 @@ START_TEST(mfplat)
     test_scheduled_items();
     test_serial_queue();
     test_periodic_callback();
+    test_event_queue();
 
     CoUninitialize();
 }
diff --git a/include/mferror.h b/include/mferror.h
index 53024caed1..54f09fefdc 100644
--- a/include/mferror.h
+++ b/include/mferror.h
@@ -66,6 +66,7 @@
 #define MF_E_ATTRIBUTENOTFOUND              _HRESULT_TYPEDEF_(0xc00d36e6)
 #define MF_E_PROPERTY_TYPE_NOT_ALLOWED      _HRESULT_TYPEDEF_(0xc00d36e7)
 #define MF_E_INVALID_WORKQUEUE              _HRESULT_TYPEDEF_(0xc00d36ff)
+#define MF_E_NO_EVENTS_AVAILABLE            _HRESULT_TYPEDEF_(0xc00d3e80)
 #define MF_E_SHUTDOWN                       _HRESULT_TYPEDEF_(0xc00d3e85)
 
 #define MF_E_TOPO_INVALID_OPTIONAL_NODE             _HRESULT_TYPEDEF_(0xc00d520e)
diff --git a/include/mfobjects.idl b/include/mfobjects.idl
index 3245d84d1b..b67aec2a61 100644
--- a/include/mfobjects.idl
+++ b/include/mfobjects.idl
@@ -553,6 +553,8 @@ interface IMFMediaEvent : IMFAttributes
     HRESULT GetValue([out] PROPVARIANT *pvValue);
 }
 
+cpp_quote("#define MF_EVENT_FLAG_NO_WAIT 0x00000001")
+
 [
     object,
     uuid(2cd0bd52-bcd5-4b89-b62c-eadc0c031e7d)
-- 
2.20.1




More information about the wine-devel mailing list