From eff5a7aebc65e98cacf0d6a0ccc6101ffa552aa0 Mon Sep 17 00:00:00 2001 From: Nikolay Sivov Date: Mon, 4 Mar 2019 13:33:43 +0300 Subject: [PATCH] mfplat: Implement media event queue. Signed-off-by: Nikolay Sivov Signed-off-by: Alexandre Julliard --- dlls/mfplat/main.c | 424 ++++++++++++++++++++++++++----------- dlls/mfplat/tests/mfplat.c | 202 +++++++++++++++++- include/mferror.h | 1 + include/mfobjects.idl | 2 + 4 files changed, 499 insertions(+), 130 deletions(-) diff --git a/dlls/mfplat/main.c b/dlls/mfplat/main.c index 0bf7cbf4c9a..0ffcd88a380 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; -static inline mfeventqueue *impl_from_IMFMediaEventQueue(IMFMediaEventQueue *iface) + CRITICAL_SECTION cs; + CONDITION_VARIABLE update_event; + struct list events; + BOOL is_shut_down; + IMFAsyncResult *subscriber; +}; + +struct queued_event { - return CONTAINING_RECORD(iface, mfeventqueue, IMFMediaEventQueue_iface); + struct list entry; + IMFMediaEvent *event; +}; + +static inline struct event_queue *impl_from_IMFMediaEventQueue(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)) + 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)) { - *out = &This->IMFMediaEventQueue_iface; + *out = &queue->IMFMediaEventQueue_iface; + IMFMediaEventQueue_AddRef(iface); + return S_OK; + } + + WARN("Unsupported %s.\n", debugstr_guid(riid)); + *out = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI eventqueue_AddRef(IMFMediaEventQueue *iface) +{ + struct event_queue *queue = impl_from_IMFMediaEventQueue(iface); + ULONG refcount = InterlockedIncrement(&queue->refcount); + + TRACE("%p, refcount %u.\n", iface, refcount); + + return refcount; +} + +static ULONG WINAPI eventqueue_Release(IMFMediaEventQueue *iface) +{ + struct event_queue *queue = impl_from_IMFMediaEventQueue(iface); + ULONG refcount = InterlockedDecrement(&queue->refcount); + + TRACE("%p, refcount %u.\n", queue, refcount); + + if (!refcount) + { + event_queue_cleanup(queue); + DeleteCriticalSection(&queue->cs); + heap_free(queue); + } + + return refcount; +} + +static HRESULT WINAPI eventqueue_GetEvent(IMFMediaEventQueue *iface, DWORD flags, IMFMediaEvent **event) +{ + struct event_queue *queue = impl_from_IMFMediaEventQueue(iface); + HRESULT hr = S_OK; + + TRACE("%p, %p.\n", iface, event); + + 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 void queue_notify_subscriber(struct event_queue *queue) +{ + if (list_empty(&queue->events) || !queue->subscriber) + return; + + MFPutWorkItemEx(MFASYNC_CALLBACK_QUEUE_STANDARD, queue->subscriber); +} + +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 { - FIXME("(%s, %p)\n", debugstr_guid(riid), out); - *out = NULL; - return E_NOINTERFACE; + hr = MFCreateAsyncResult(NULL, callback, state, &queue->subscriber); + if (SUCCEEDED(hr)) + queue_notify_subscriber(queue); } - IUnknown_AddRef((IUnknown*)*out); + LeaveCriticalSection(&queue->cs); + + return hr; +} + +static HRESULT WINAPI eventqueue_EndGetEvent(IMFMediaEventQueue *iface, IMFAsyncResult *result, IMFMediaEvent **event) +{ + struct event_queue *queue = impl_from_IMFMediaEventQueue(iface); + HRESULT hr = E_FAIL; + + TRACE("%p, %p, %p.\n", iface, result, event); + + 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 eventqueue_queue_event(struct event_queue *queue, IMFMediaEvent *event) +{ + struct queued_event *queued_event; + HRESULT hr = S_OK; + + queued_event = heap_alloc(sizeof(*queued_event)); + if (!queued_event) + return E_OUTOFMEMORY; + + queued_event->event = event; + + 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 eventqueue_QueueEvent(IMFMediaEventQueue *iface, IMFMediaEvent *event) +{ + struct event_queue *queue = impl_from_IMFMediaEventQueue(iface); + + TRACE("%p, %p.\n", iface, event); + + return eventqueue_queue_event(queue, event); +} + +static HRESULT WINAPI eventqueue_QueueEventParamVar(IMFMediaEventQueue *iface, MediaEventType event_type, + REFGUID extended_type, HRESULT status, const PROPVARIANT *value) +{ + struct event_queue *queue = impl_from_IMFMediaEventQueue(iface); + IMFMediaEvent *event; + HRESULT hr; + + TRACE("%p, %d, %s, %#x, %p\n", iface, event_type, debugstr_guid(extended_type), status, value); + + 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 eventqueue_QueueEventParamUnk(IMFMediaEventQueue *iface, MediaEventType event_type, + REFGUID extended_type, HRESULT status, IUnknown *unk) +{ + struct event_queue *queue = impl_from_IMFMediaEventQueue(iface); + IMFMediaEvent *event; + PROPVARIANT value; + HRESULT hr; + + TRACE("%p, %d, %s, %#x, %p.\n", iface, event_type, debugstr_guid(extended_type), status, unk); + + 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 HRESULT WINAPI eventqueue_Shutdown(IMFMediaEventQueue *iface) +{ + 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 ULONG WINAPI mfeventqueue_AddRef(IMFMediaEventQueue *iface) +static const IMFMediaEventQueueVtbl eventqueuevtbl = { - mfeventqueue *This = impl_from_IMFMediaEventQueue(iface); - ULONG ref = InterlockedIncrement(&This->ref); - - TRACE("(%p) ref=%u\n", This, ref); - - return ref; -} - -static ULONG WINAPI mfeventqueue_Release(IMFMediaEventQueue *iface) -{ - mfeventqueue *This = impl_from_IMFMediaEventQueue(iface); - ULONG ref = InterlockedDecrement(&This->ref); - - TRACE("(%p) ref=%u\n", This, ref); - - if (!ref) - { - HeapFree(GetProcessHeap(), 0, This); - } - - return ref; -} - -static HRESULT WINAPI mfeventqueue_GetEvent(IMFMediaEventQueue *iface, DWORD flags, IMFMediaEvent **event) -{ - mfeventqueue *This = impl_from_IMFMediaEventQueue(iface); - - FIXME("%p, %p\n", This, event); - - return E_NOTIMPL; -} - -static HRESULT WINAPI mfeventqueue_BeginGetEvent(IMFMediaEventQueue *iface, IMFAsyncCallback *callback, IUnknown *state) -{ - mfeventqueue *This = impl_from_IMFMediaEventQueue(iface); - - FIXME("%p, %p, %p\n", This, callback, state); - - return E_NOTIMPL; -} - -static HRESULT WINAPI mfeventqueue_EndGetEvent(IMFMediaEventQueue *iface, IMFAsyncResult *result, IMFMediaEvent **event) -{ - mfeventqueue *This = impl_from_IMFMediaEventQueue(iface); - - FIXME("%p, %p, %p\n", This, result, event); - - return E_NOTIMPL; -} - -static HRESULT WINAPI mfeventqueue_QueueEvent(IMFMediaEventQueue *iface, IMFMediaEvent *event) -{ - mfeventqueue *This = impl_from_IMFMediaEventQueue(iface); - - FIXME("%p, %p\n", This, event); - - return E_NOTIMPL; -} - -static HRESULT WINAPI mfeventqueue_QueueEventParamVar(IMFMediaEventQueue *iface, MediaEventType met, - REFGUID type, HRESULT status, const PROPVARIANT *value) -{ - mfeventqueue *This = impl_from_IMFMediaEventQueue(iface); - - FIXME("%p, %d, %s, 0x%08x, %p\n", This, met, debugstr_guid(type), status, value); - - return E_NOTIMPL; -} - -static HRESULT WINAPI mfeventqueue_QueueEventParamUnk(IMFMediaEventQueue *iface, MediaEventType met, REFGUID type, - HRESULT status, IUnknown *unk) -{ - mfeventqueue *This = impl_from_IMFMediaEventQueue(iface); - - FIXME("%p, %d, %s, 0x%08x, %p\n", This, met, debugstr_guid(type), status, unk); - - return E_NOTIMPL; -} - -static HRESULT WINAPI mfeventqueue_Shutdown(IMFMediaEventQueue *iface) -{ - mfeventqueue *This = impl_from_IMFMediaEventQueue(iface); - - FIXME("%p\n", This); - - return E_NOTIMPL; -} - -static const IMFMediaEventQueueVtbl mfeventqueue_vtbl = -{ - mfeventqueue_QueryInterface, - mfeventqueue_AddRef, - mfeventqueue_Release, - mfeventqueue_GetEvent, - mfeventqueue_BeginGetEvent, - mfeventqueue_EndGetEvent, - mfeventqueue_QueueEvent, - mfeventqueue_QueueEventParamVar, - mfeventqueue_QueueEventParamUnk, - mfeventqueue_Shutdown + 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 e413d61dfa9..4405bd9282f 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 53024caed15..54f09fefdcc 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 3245d84d1bd..b67aec2a615 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)