From: Charlotte Pabst cpabst@codeweavers.com
--- dlls/mfsrcsnk/tests/mfsrcsnk.c | 412 +++++++++++++++++++++++++++++++++ 1 file changed, 412 insertions(+)
diff --git a/dlls/mfsrcsnk/tests/mfsrcsnk.c b/dlls/mfsrcsnk/tests/mfsrcsnk.c index 1aba4094c62..b7645d113b2 100644 --- a/dlls/mfsrcsnk/tests/mfsrcsnk.c +++ b/dlls/mfsrcsnk/tests/mfsrcsnk.c @@ -204,6 +204,418 @@ static void test_wave_sink(void) IMFByteStream_Release(bytestream); }
+struct source_create_callback +{ + IMFAsyncCallback iface; + LONG refcount; + + IMFByteStreamHandler *handler; + HRESULT hr; + MF_OBJECT_TYPE type; + IUnknown *object; + HANDLE event; +}; + +struct source_create_callback *source_create_callback_from_iface(IMFAsyncCallback *iface) +{ + return CONTAINING_RECORD(iface, struct source_create_callback, iface); +} + +static HRESULT WINAPI source_create_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 source_create_callback_AddRef(IMFAsyncCallback *iface) +{ + struct source_create_callback *callback = source_create_callback_from_iface(iface); + return InterlockedIncrement(&callback->refcount); +} + +static ULONG WINAPI source_create_callback_Release(IMFAsyncCallback *iface) +{ + struct source_create_callback *callback = source_create_callback_from_iface(iface); + ULONG refcount = InterlockedDecrement(&callback->refcount); + if (refcount == 0) + { + if (callback->object) + IUnknown_Release(callback->object); + IMFByteStreamHandler_Release(callback->handler); + CloseHandle(callback->event); + free(callback); + } + return refcount; +} + +static HRESULT WINAPI source_create_callback_GetParameters(IMFAsyncCallback *iface, DWORD *flags, DWORD *queue) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI source_create_callback_stream_Invoke(IMFAsyncCallback *iface, IMFAsyncResult *result) +{ + struct source_create_callback *callback = source_create_callback_from_iface(iface); + callback->hr = IMFByteStreamHandler_EndCreateObject(callback->handler, result, &callback->type, &callback->object); + SetEvent(callback->event); + return callback->hr; +} + +static const IMFAsyncCallbackVtbl source_create_callback_vtbl = { + &source_create_callback_QueryInterface, + &source_create_callback_AddRef, + &source_create_callback_Release, + &source_create_callback_GetParameters, + &source_create_callback_stream_Invoke, +}; + +static HRESULT create_source(const GUID *guid_handler, IMFByteStream *stream, IMFMediaSource **source) +{ + HRESULT hr; + IMFByteStreamHandler *handler; + struct source_create_callback *callback; + + if (!(callback = calloc(1, sizeof *callback))) + return E_OUTOFMEMORY; + hr = CoCreateInstance(guid_handler, NULL, CLSCTX_INPROC_SERVER, &IID_IMFByteStreamHandler, (void **) &handler); + if (FAILED(hr)) + { + free(callback); + return hr; + } + callback->iface.lpVtbl = &source_create_callback_vtbl; + callback->refcount = 1; + callback->handler = handler; + callback->object = NULL; + callback->type = MF_OBJECT_INVALID; + callback->hr = E_PENDING; + callback->event = CreateEventW(NULL, FALSE, FALSE, NULL); + + hr = IMFByteStreamHandler_BeginCreateObject(callback->handler, stream, NULL, MF_RESOLUTION_MEDIASOURCE, NULL, NULL, &callback->iface, NULL); + if (FAILED(hr)) + goto done; + + WaitForSingleObject(callback->event, INFINITE); + if (FAILED(hr = callback->hr)) + goto done; + if (callback->type != MF_OBJECT_MEDIASOURCE) + { + hr = E_UNEXPECTED; + goto done; + } + + hr = S_OK; + *source = (IMFMediaSource *) callback->object; + callback->object = NULL; + +done: + IMFAsyncCallback_Release(&callback->iface); + return hr; +} + +static IMFByteStream *create_byte_stream(const BYTE *data, ULONG data_len) +{ + IMFByteStream *stream; + HRESULT hr; + + hr = MFCreateTempFile(MF_ACCESSMODE_READWRITE, MF_OPENMODE_DELETE_IF_EXIST, MF_FILEFLAGS_NONE, &stream); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFByteStream_Write(stream, data, data_len, &data_len); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFByteStream_SetCurrentPosition(stream, 0); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + return stream; +} + +static IMFByteStream *create_resource_byte_stream(const WCHAR *name) +{ + const BYTE *resource_data; + ULONG resource_len; + HRSRC resource; + + resource = FindResourceW(NULL, name, (const WCHAR *)RT_RCDATA); + ok(resource != 0, "FindResourceW %s failed, error %lu\n", debugstr_w(name), GetLastError()); + resource_data = LockResource(LoadResource(GetModuleHandleW(NULL), resource)); + resource_len = SizeofResource(GetModuleHandleW(NULL), resource); + + return create_byte_stream(resource_data, resource_len); +} + +struct event_callback +{ + IMFAsyncCallback iface; + LONG refcount; + + /* to allow synchronously waiting for events with timeout */ + IMFMediaEvent *event; + CRITICAL_SECTION cs; + CONDITION_VARIABLE produced; + CONDITION_VARIABLE consumed; +}; + +static struct event_callback *event_callback_from_iface(IMFAsyncCallback *iface) +{ + return CONTAINING_RECORD(iface, struct event_callback, iface); +} + +static HRESULT WINAPI event_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 event_callback_AddRef(IMFAsyncCallback *iface) +{ + struct event_callback *callback = event_callback_from_iface(iface); + return InterlockedIncrement(&callback->refcount); +} + +static ULONG WINAPI event_callback_Release(IMFAsyncCallback *iface) +{ + struct event_callback *callback = event_callback_from_iface(iface); + ULONG refcount = InterlockedDecrement(&callback->refcount); + if (refcount == 0) + { + DeleteCriticalSection(&callback->cs); + if (callback->event) + IMFMediaEvent_Release(callback->event); + free(callback); + } + return refcount; +} + +static HRESULT WINAPI event_callback_GetParameters(IMFAsyncCallback *iface, DWORD *flags, DWORD *queue) +{ + return E_NOTIMPL; +} + +BOOL event_callback_send_event(struct event_callback *callback, IMFMediaEvent *event) +{ + BOOL status = FALSE; + EnterCriticalSection(&callback->cs); + if (callback->event) + SleepConditionVariableCS(&callback->consumed, &callback->cs, INFINITE); + if (!callback->event) + { + status = TRUE; + callback->event = event; + WakeConditionVariable(&callback->produced); + } + LeaveCriticalSection(&callback->cs); + return status; +} + +IMFMediaEvent *event_callback_recv_event(struct event_callback *callback, DWORD timeout) +{ + IMFMediaEvent *event = NULL; + EnterCriticalSection(&callback->cs); + if (!callback->event) + SleepConditionVariableCS(&callback->produced, &callback->cs, timeout); + if (callback->event) + { + event = callback->event; + callback->event = NULL; + WakeConditionVariable(&callback->consumed); + } + LeaveCriticalSection(&callback->cs); + return event; +} + +void event_callback_shutdown(struct event_callback *callback) +{ + WakeAllConditionVariable(&callback->consumed); +} + +static HRESULT WINAPI event_callback_Invoke(IMFAsyncCallback *iface, IMFAsyncResult *result) +{ + struct event_callback *callback = event_callback_from_iface(iface); + IMFMediaEventGenerator *source; + IMFMediaEvent *event; + MediaEventType type; + PROPVARIANT value; + HRESULT hr; + + ok(result != NULL, "Unexpected result object.\n"); + + hr = IMFAsyncResult_GetState(result, (IUnknown **) &source); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMFMediaEventGenerator_EndGetEvent(source, result, &event); + if (hr == MF_E_SHUTDOWN) + { + IMFMediaEventGenerator_Release(source); + return hr; + } + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + if (!event_callback_send_event(callback, event)) + { + IMFMediaEvent_Release(event); + IMFMediaEventGenerator_Release(source); + return MF_E_SHUTDOWN; + } + + hr = IMFMediaEvent_GetType(event, &type); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + if (type == MENewStream) + { + hr = IMFMediaEvent_GetValue(event, &value); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(value.vt == VT_UNKNOWN, "Unexpected vt %d.\n", value.vt); + ok(!!value.punkVal, "Missing stream.\n"); + + hr = IMFMediaStream_BeginGetEvent((IMFMediaStream *) value.punkVal, iface, value.punkVal); + ok(hr == S_OK || hr == MF_S_MULTIPLE_BEGIN, "Unexpected hr %#lx.\n", hr); + + PropVariantClear(&value); + } + + hr = IMFMediaEventGenerator_BeginGetEvent(source, iface, (IUnknown *) source); + if (hr != MF_E_SHUTDOWN) + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + IMFMediaEventGenerator_Release(source); + + return hr; +} + +static const IMFAsyncCallbackVtbl event_callback_vtbl = +{ + event_callback_QueryInterface, + event_callback_AddRef, + event_callback_Release, + event_callback_GetParameters, + event_callback_Invoke, +}; + +static struct event_callback *create_event_callback(void) +{ + struct event_callback *callback; + + if (!(callback = calloc(1, sizeof(*callback)))) + return NULL; + + callback->refcount = 1; + callback->iface.lpVtbl = &event_callback_vtbl; + InitializeCriticalSection(&callback->cs); + InitializeConditionVariable(&callback->produced); + InitializeConditionVariable(&callback->consumed); + + return callback; +} + +/* assumes a single stream for now, this could be expanded upon later if needed */ +#define wait_for_source_start(a, b) wait_for_source_start_(__LINE__, a, b) +static void wait_for_source_start_(int line, struct event_callback *callback, + IMFMediaStream **stream) +{ + BOOL got_stream = FALSE; + BOOL source_started = FALSE; + BOOL stream_started = FALSE; + IMFMediaEvent *event; + MediaEventType type; + PROPVARIANT value; + HRESULT hr; + + while (!(source_started && got_stream && stream_started) + && (event = event_callback_recv_event(callback, 1000))) + { + hr = IMFMediaEvent_GetType(event, &type); + ok_(__FILE__, line)(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + switch (type) + { + case MESourceStarted: + case MESourceSeeked: + ok_(__FILE__, line)(!source_started, "Unexpected source start.\n"); + source_started = TRUE; + break; + + case MEStreamStarted: + case MEStreamSeeked: + ok_(__FILE__, line)(got_stream && !stream_started, "Unexpected stream start.\n"); + stream_started = TRUE; + break; + + case MENewStream: + case MEUpdatedStream: + ok_(__FILE__, line)(!got_stream, "Unexpected stream.\n"); + got_stream = TRUE; + + if (stream) + { + hr = IMFMediaEvent_GetValue(event, &value); + ok_(__FILE__, line)(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok_(__FILE__, line)(value.vt == VT_UNKNOWN, "Unexpected vt %d.\n", value.vt); + ok_(__FILE__, line)(!!value.punkVal, "Missing stream.\n"); + + *stream = (IMFMediaStream *) value.punkVal; + IMFMediaStream_AddRef(*stream); + PropVariantClear(&value); + } + + break; + } + + IMFMediaEvent_Release(event); + } + + ok_(__FILE__, line)(source_started, "Expected source start.\n"); + ok_(__FILE__, line)(got_stream, "Expected stream.\n"); + ok_(__FILE__, line)(stream_started, "Expected stream start.\n"); +} + +#define wait_for_source_stop(a) wait_for_source_stop_(__LINE__, a) +static void wait_for_source_stop_(int line, struct event_callback *callback) +{ + BOOL source_stopped = FALSE; + BOOL stream_stopped = FALSE; + IMFMediaEvent *event; + MediaEventType type; + HRESULT hr; + + while (!(source_stopped && stream_stopped) + && (event = event_callback_recv_event(callback, 1000))) + { + hr = IMFMediaEvent_GetType(event, &type); + ok_(__FILE__, line)(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + switch (type) + { + case MESourceStopped: + ok_(__FILE__, line)(!source_stopped, "Unexpected MESourceStopped.\n"); + source_stopped = TRUE; + break; + + case MEStreamStopped: + ok_(__FILE__, line)(!stream_stopped, "Unexpected MEStreamStopped.\n"); + stream_stopped = TRUE; + break; + } + + IMFMediaEvent_Release(event); + } + + ok_(__FILE__, line)(source_stopped, "Expected MESourceStopped.\n"); + ok_(__FILE__, line)(stream_stopped, "Expected MEStreamStopped.\n"); +} + START_TEST(mfsrcsnk) { HRESULT hr;