From: Brendan McGrath bmcgrath@codeweavers.com
--- dlls/mf/tests/Makefile.in | 3 +- dlls/mf/tests/evr.c | 1553 +++++++++++++++++++++++++++++++++++++ 2 files changed, 1555 insertions(+), 1 deletion(-) create mode 100644 dlls/mf/tests/evr.c
diff --git a/dlls/mf/tests/Makefile.in b/dlls/mf/tests/Makefile.in index 432400d4efc..cd7e1aaa809 100644 --- a/dlls/mf/tests/Makefile.in +++ b/dlls/mf/tests/Makefile.in @@ -1,7 +1,8 @@ TESTDLL = mf.dll -IMPORTS = mf mfplat dmoguids mfuuid strmiids uuid wmcodecdspuuid ole32 user32 propsys msdmo d3d9 d3d11 dxva2 msvfw32 +IMPORTS = mf mfplat dmoguids mfuuid strmiids uuid wmcodecdspuuid ole32 user32 propsys msdmo d3d9 d3d11 dxva2 msvfw32 dxguid evr
SOURCES = \ + evr.c \ mf.c \ resource.rc \ topology.c \ diff --git a/dlls/mf/tests/evr.c b/dlls/mf/tests/evr.c new file mode 100644 index 00000000000..fa4e6d3beb4 --- /dev/null +++ b/dlls/mf/tests/evr.c @@ -0,0 +1,1553 @@ +/* + * Copyright 2024 Brendan McGrath + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#define COBJMACROS +#include "d3d9.h" +#include "dxva2api.h" +#include "evr.h" +#include "evcode.h" +#include "mferror.h" + +#include "mf_test.h" + +#include "wine/test.h" + +static BOOL hit_not_set = FALSE; +static BOOL hit_set_again = FALSE; + +static HWND create_window(void) +{ + RECT r = {0, 0, 640, 480}; + + AdjustWindowRect(&r, WS_OVERLAPPEDWINDOW | WS_VISIBLE, FALSE); + + return CreateWindowA("static", "mf_test", WS_OVERLAPPEDWINDOW | WS_VISIBLE, + 0, 0, r.right - r.left, r.bottom - r.top, NULL, NULL, NULL, NULL); +} + +struct event_callback +{ + IMFAsyncCallback IMFAsyncCallback_iface; + LONG refcount; + + HANDLE event; + IMFMediaEvent *media_event; + BOOL check_media_event; +}; + +static struct event_callback *impl_from_event_IMFAsyncCallback(IMFAsyncCallback *iface) +{ + return CONTAINING_RECORD(iface, struct event_callback, IMFAsyncCallback_iface); +} + +static HRESULT WINAPI event_callback_QueryInterface(IMFAsyncCallback *iface, REFIID riid, void **obj) +{ + if (IsEqualIID(riid, &IID_IMFAsyncCallback) || + IsEqualIID(riid, &IID_IUnknown)) + { + *obj = iface; + } + else + { + *obj = NULL; + return E_NOINTERFACE; + } + + IMFAsyncCallback_AddRef(iface); + return S_OK; +} + +static ULONG WINAPI event_callback_AddRef(IMFAsyncCallback *iface) +{ + struct event_callback *callback = impl_from_event_IMFAsyncCallback(iface); + return InterlockedIncrement(&callback->refcount); +} + +static ULONG WINAPI event_callback_Release(IMFAsyncCallback *iface) +{ + struct event_callback *callback = impl_from_event_IMFAsyncCallback(iface); + ULONG refcount = InterlockedDecrement(&callback->refcount); + + if (!refcount) + { + if (callback->media_event) + IMFMediaEvent_Release(callback->media_event); + CloseHandle(callback->event); + free(callback); + } + + return refcount; +} + +static HRESULT WINAPI event_callback_GetParameters(IMFAsyncCallback *iface, DWORD *flags, DWORD *queue) +{ + ok(flags != NULL && queue != NULL, "Unexpected arguments.\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI event_callback_Invoke(IMFAsyncCallback *iface, IMFAsyncResult *result) +{ + struct event_callback *callback = impl_from_event_IMFAsyncCallback(iface); + IUnknown *object; + HRESULT hr; + + ok(result != NULL, "Unexpected result object.\n"); + + if (callback->media_event) + IMFMediaEvent_Release(callback->media_event); + + if (callback->check_media_event) + { + hr = IMFAsyncResult_GetObject(result, &object); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + + hr = IMFAsyncResult_GetState(result, &object); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaEventGenerator_EndGetEvent((IMFMediaEventGenerator *)object, + result, &callback->media_event); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + IUnknown_Release(object); + } + + SetEvent(callback->event); + + return S_OK; +} + +static const IMFAsyncCallbackVtbl event_callback_vtbl = +{ + event_callback_QueryInterface, + event_callback_AddRef, + event_callback_Release, + event_callback_GetParameters, + event_callback_Invoke, +}; + +static IMFAsyncCallback *create_event_callback(BOOL check_media_event) +{ + struct event_callback *callback; + + callback = malloc(sizeof(*callback)); + ok(!!callback, "Failed to allocate memory for callback\n"); + callback->refcount = 1; + callback->check_media_event = check_media_event; + callback->IMFAsyncCallback_iface.lpVtbl = &event_callback_vtbl; + callback->event = CreateEventW(NULL, FALSE, FALSE, NULL); + ok(!!callback->event, "CreateEventW failed, error %lu\n", GetLastError()); + callback->media_event = NULL; + + return &callback->IMFAsyncCallback_iface; +} + +#define wait_media_event_until_blocking(a, b, c, d, e, f) wait_media_event_until_blocking_(__LINE__, a, b, c, d, e, f) +static HRESULT wait_media_event_until_blocking_(int line, IMFMediaSession *session, IMFAsyncCallback *callback, + MediaEventType expect_type, DWORD timeout, PROPVARIANT *value, IMFMediaEvent **event) +{ + struct event_callback *impl = impl_from_event_IMFAsyncCallback(callback); + PROPVARIANT v; + MediaEventType type; + HRESULT hr, status; + DWORD ret; + GUID guid; + + do + { + hr = IMFMediaSession_BeginGetEvent(session, &impl->IMFAsyncCallback_iface, (IUnknown *)session); + ok_(__FILE__, line)(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ret = WaitForSingleObject(impl->event, timeout); + if (ret == WAIT_TIMEOUT) return WAIT_TIMEOUT; + hr = IMFMediaEvent_GetType(impl->media_event, &type); + ok_(__FILE__, line)(hr == S_OK, "Unexpected hr %#lx.\n", hr); + } while (type != expect_type); + + ok_(__FILE__, line)(type == expect_type, "got type %lu\n", type); + + hr = IMFMediaEvent_GetExtendedType(impl->media_event, &guid); + ok_(__FILE__, line)(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok_(__FILE__, line)(IsEqualGUID(&guid, &GUID_NULL), "got extended type %s\n", debugstr_guid(&guid)); + + PropVariantInit(&v); + hr = IMFMediaEvent_GetValue(impl->media_event, &v); + ok_(__FILE__, line)(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + if (value) + *value = v; + else + PropVariantClear(&v); + + hr = IMFMediaEvent_GetStatus(impl->media_event, &status); + ok_(__FILE__, line)(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + if (event) + IMFMediaEvent_AddRef(*event = impl->media_event); + + return status; +} + +struct sample_callback +{ + IMFAsyncCallback IMFAsyncCallback_iface; + LONG refcount; + + IMFMediaEventQueue *media_queue; + IMFMediaEventQueue *stream_queue; + const GUID *subtype; + UINT32 width; + UINT32 height; +}; + +static struct sample_callback* impl_from_sample_IMFAsyncCallback(IMFAsyncCallback *iface) +{ + return CONTAINING_RECORD(iface, struct sample_callback, IMFAsyncCallback_iface); +} + +static HRESULT WINAPI sample_callback_QueryInterface(IMFAsyncCallback *iface, REFIID riid, void **obj) +{ + struct sample_callback *callback = impl_from_sample_IMFAsyncCallback(iface); + + if (IsEqualIID(riid, &IID_IMFAsyncCallback) || + IsEqualIID(riid, &IID_IUnknown)) + { + *obj = &callback->IMFAsyncCallback_iface; + } + else + { + *obj = NULL; + return E_NOINTERFACE; + } + + IMFAsyncCallback_AddRef(iface); + return S_OK; +} + +static ULONG WINAPI sample_callback_AddRef(IMFAsyncCallback *iface) +{ + struct sample_callback *callback = impl_from_sample_IMFAsyncCallback(iface); + ULONG refcount = InterlockedIncrement(&callback->refcount); + return refcount; +} + +static ULONG WINAPI sample_callback_Release(IMFAsyncCallback *iface) +{ + struct sample_callback *callback = impl_from_sample_IMFAsyncCallback(iface); + ULONG refcount = InterlockedDecrement(&callback->refcount); + + if (!refcount) + { + IMFMediaEventQueue_Release(callback->media_queue); + IMFMediaEventQueue_Release(callback->stream_queue); + free(callback); + } + + return refcount; +} + +static HRESULT WINAPI sample_callback_GetParameters(IMFAsyncCallback *iface, DWORD *flags, DWORD *queue) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI sample_callback_Invoke(IMFAsyncCallback *iface, IMFAsyncResult *result) +{ + static LONGLONG time = 0; + struct sample_callback *callback; + IMFMediaBuffer *buffer; + IMFSample *sample; + PROPVARIANT value; + HRESULT hr; + + callback = impl_from_sample_IMFAsyncCallback(iface); + hr = MFCreateSample(&sample); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = MFCreate2DMediaBuffer(callback->width, callback->height, callback->subtype->Data1, FALSE, &buffer); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFSample_AddBuffer(sample, buffer); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFSample_SetSampleTime(sample, time+=333333); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFSample_SetSampleDuration(sample, 333333); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + value.vt = VT_UNKNOWN; + value.punkVal = (IUnknown*)sample; + hr = IMFMediaEventQueue_QueueEventParamVar(callback->stream_queue, MEMediaSample, &GUID_NULL, S_OK, &value); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + IMFMediaBuffer_Release(buffer); + PropVariantClear(&value); + if (time >= 1000000) + { + hr = IMFMediaEventQueue_QueueEventParamVar(callback->stream_queue, MEEndOfStream, &GUID_NULL, S_OK, &value); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaEventQueue_QueueEventParamVar(callback->media_queue, MEEndOfPresentation, &GUID_NULL, S_OK, &value); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + } + + return S_OK; +} + +static IMFAsyncCallbackVtbl sample_callback_vtbl = +{ + sample_callback_QueryInterface, + sample_callback_AddRef, + sample_callback_Release, + sample_callback_GetParameters, + sample_callback_Invoke, +}; + +static struct sample_callback *create_sample_callback(IMFMediaEventQueue *media_queue, IMFMediaEventQueue *stream_queue, + const GUID *subtype, UINT32 width, UINT32 height) +{ + struct sample_callback *callback; + + callback = malloc(sizeof(*callback)); + ok(!!callback, "Failed to allocate memory for callback\n"); + callback->IMFAsyncCallback_iface.lpVtbl = &sample_callback_vtbl; + callback->refcount = 1; + IMFMediaEventQueue_AddRef(callback->media_queue = media_queue); + IMFMediaEventQueue_AddRef(callback->stream_queue = stream_queue); + callback->subtype = subtype; + callback->width = width; + callback->height = height; + return callback; +} + +struct test_source +{ + IMFMediaSource IMFMediaSource_iface; + IMFMediaStream IMFMediaStream_iface; + LONG refcount; + + IMFMediaType *type; + IMFStreamDescriptor *sd; + IMFPresentationDescriptor *pd; + IMFMediaEventQueue *media_queue; + IMFMediaEventQueue *stream_queue; + const GUID *subtype; + UINT32 width; + UINT32 height; +}; + +static struct test_source *impl_from_IMFMediaStream(IMFMediaStream *iface) +{ + return CONTAINING_RECORD(iface, struct test_source, IMFMediaStream_iface); +} + +static HRESULT WINAPI test_stream_QueryInterface(IMFMediaStream *iface, REFIID riid, void **out) +{ + struct test_source *source = impl_from_IMFMediaStream(iface); + if (IsEqualIID(riid, &IID_IMFMediaStream) + || IsEqualIID(riid, &IID_IMFMediaEventGenerator) + || IsEqualIID(riid, &IID_IUnknown)) + { + *out = &source->IMFMediaStream_iface; + } + else + { + *out = NULL; + return E_NOINTERFACE; + } + + IMFMediaStream_AddRef(iface); + return S_OK; +} + +static ULONG WINAPI test_stream_AddRef(IMFMediaStream *iface) +{ + struct test_source *source = impl_from_IMFMediaStream(iface); + return IMFMediaSource_AddRef(&source->IMFMediaSource_iface); +} + +static ULONG WINAPI test_stream_Release(IMFMediaStream *iface) +{ + struct test_source *source = impl_from_IMFMediaStream(iface); + return IMFMediaSource_Release(&source->IMFMediaSource_iface); +} + +static HRESULT WINAPI test_stream_GetEvent(IMFMediaStream *iface, DWORD flags, IMFMediaEvent **event) +{ + struct test_source *source; + HRESULT hr; + + source = impl_from_IMFMediaStream(iface); + hr = IMFMediaEventQueue_GetEvent(source->stream_queue, flags, event); + ok(hr == MF_E_NO_EVENTS_AVAILABLE, "Unexpected hr %#lx.\n", hr); + return hr; +} + +static HRESULT WINAPI test_stream_BeginGetEvent(IMFMediaStream *iface, IMFAsyncCallback *callback, IUnknown *state) +{ + struct test_source *source; + HRESULT hr; + + source = impl_from_IMFMediaStream(iface); + hr = IMFMediaEventQueue_BeginGetEvent(source->stream_queue, callback, state); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + return hr; +} + +static HRESULT WINAPI test_stream_EndGetEvent(IMFMediaStream *iface, IMFAsyncResult *result, IMFMediaEvent **event) +{ + struct test_source *source; + HRESULT hr; + + source = impl_from_IMFMediaStream(iface); + hr = IMFMediaEventQueue_EndGetEvent(source->stream_queue, result, event); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + return hr; +} + +static HRESULT WINAPI test_stream_QueueEvent(IMFMediaStream *iface, MediaEventType event_type, REFGUID ext_type, + HRESULT hr, const PROPVARIANT *value) +{ + struct test_source *source; + HRESULT res; + + source = impl_from_IMFMediaStream(iface); + res = IMFMediaEventQueue_QueueEventParamVar(source->stream_queue, event_type, ext_type, hr, value); + ok(res == S_OK, "Unexpected hr %#lx.\n", hr); + return hr; +} + +static HRESULT WINAPI test_stream_GetMediaSource(IMFMediaStream *iface, IMFMediaSource **source) +{ + struct test_source *test_source = impl_from_IMFMediaStream(iface); + IMFMediaSource_AddRef(*source = &test_source->IMFMediaSource_iface); + return S_OK; +} + +static HRESULT WINAPI test_stream_GetStreamDescriptor(IMFMediaStream *iface, IMFStreamDescriptor **sd) +{ + struct test_source *source = impl_from_IMFMediaStream(iface); + IMFStreamDescriptor_AddRef(*sd = source->sd); + return S_OK; +} + +static HRESULT WINAPI test_stream_RequestSample(IMFMediaStream *iface, IUnknown *token) +{ + struct test_source *source = impl_from_IMFMediaStream(iface); + struct sample_callback *callback = create_sample_callback(source->media_queue, source->stream_queue, + source->subtype, source->width, source->height); + HRESULT hr; + + hr = MFScheduleWorkItem(&callback->IMFAsyncCallback_iface, token, 10000, NULL); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + IMFAsyncCallback_Release(&callback->IMFAsyncCallback_iface); + return S_OK; +} + +IMFMediaStreamVtbl test_stream_vtbl = +{ + test_stream_QueryInterface, + test_stream_AddRef, + test_stream_Release, + test_stream_GetEvent, + test_stream_BeginGetEvent, + test_stream_EndGetEvent, + test_stream_QueueEvent, + test_stream_GetMediaSource, + test_stream_GetStreamDescriptor, + test_stream_RequestSample, +}; + +static struct test_source *impl_from_IMFMediaSource(IMFMediaSource *iface) +{ + return CONTAINING_RECORD(iface, struct test_source, IMFMediaSource_iface); +} + +static HRESULT WINAPI test_source_QueryInterface(IMFMediaSource *iface, REFIID riid, void **out) +{ + if (IsEqualIID(riid, &IID_IMFMediaSource) + || IsEqualIID(riid, &IID_IMFMediaEventGenerator) + || IsEqualIID(riid, &IID_IUnknown)) + { + *out = iface; + } + else + { + *out = NULL; + return E_NOINTERFACE; + } + + IMFMediaSource_AddRef(iface); + return S_OK; +} + +static ULONG WINAPI test_source_AddRef(IMFMediaSource *iface) +{ + struct test_source *source = impl_from_IMFMediaSource(iface); + return InterlockedIncrement(&source->refcount); +} + +static ULONG WINAPI test_source_Release(IMFMediaSource *iface) +{ + struct test_source *source = impl_from_IMFMediaSource(iface); + ULONG refcount = InterlockedDecrement(&source->refcount); + + if (!refcount) + { + IMFMediaEventQueue_Shutdown(source->media_queue); + IMFMediaEventQueue_Shutdown(source->stream_queue); + IMFPresentationDescriptor_Release(source->pd); + IMFStreamDescriptor_Release(source->sd); + IMFMediaType_Release(source->type); + IMFMediaEventQueue_Release(source->media_queue); + IMFMediaEventQueue_Release(source->stream_queue); + free(source); + } + + return refcount; +} + +static HRESULT WINAPI test_source_GetEvent(IMFMediaSource *iface, DWORD flags, IMFMediaEvent **event) +{ + struct test_source *source; + HRESULT hr; + + source = impl_from_IMFMediaSource(iface); + hr = IMFMediaEventQueue_GetEvent(source->media_queue, flags, event); + ok(hr == MF_E_NO_EVENTS_AVAILABLE, "Unexpected hr %#lx.\n", hr); + return hr; +} + +static HRESULT WINAPI test_source_BeginGetEvent(IMFMediaSource *iface, IMFAsyncCallback *callback, IUnknown *state) +{ + struct test_source *source; + HRESULT hr; + + source = impl_from_IMFMediaSource(iface); + hr = IMFMediaEventQueue_BeginGetEvent(source->media_queue, callback, state); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + return hr; +} + +static HRESULT WINAPI test_source_EndGetEvent(IMFMediaSource *iface, IMFAsyncResult *result, IMFMediaEvent **event) +{ + struct test_source *source; + HRESULT hr; + + source = impl_from_IMFMediaSource(iface); + hr = IMFMediaEventQueue_EndGetEvent(source->media_queue, result, event); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + return hr; +} + +static HRESULT WINAPI test_source_QueueEvent(IMFMediaSource *iface, MediaEventType event_type, REFGUID ext_type, + HRESULT hr, const PROPVARIANT *value) +{ + struct test_source *source; + HRESULT res; + + source = impl_from_IMFMediaSource(iface); + res = IMFMediaEventQueue_QueueEventParamVar(source->media_queue, event_type, ext_type, hr, value); + ok(res == S_OK, "Unexpected hr %#lx.\n", hr); + return hr; +} + +static HRESULT WINAPI test_source_GetCharacteristics(IMFMediaSource *iface, DWORD *flags) +{ + *flags = 0; + return S_OK; +} + +static HRESULT WINAPI test_source_CreatePresentationDescriptor(IMFMediaSource *iface, IMFPresentationDescriptor **pd) +{ + HRESULT hr; + struct test_source *source = impl_from_IMFMediaSource(iface); + hr = IMFPresentationDescriptor_Clone(source->pd, pd); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + return hr; +} + +static HRESULT WINAPI test_source_Start(IMFMediaSource *iface, IMFPresentationDescriptor *pd, const GUID *time_format, + const PROPVARIANT *start_position) +{ + struct test_source *source; + PROPVARIANT value; + HRESULT hr; + + source = impl_from_IMFMediaSource(iface); + value.vt = VT_UNKNOWN; + value.punkVal = (IUnknown*) &source->IMFMediaStream_iface; + hr = IMFMediaSource_QueueEvent(iface, MENewStream, &GUID_NULL, S_OK, &value); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + value.vt = VT_I8; + value.hVal.QuadPart = 0; + hr = IMFMediaSource_QueueEvent(iface, MESourceStarted, &GUID_NULL, S_OK, &value); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaStream_QueueEvent(&source->IMFMediaStream_iface, MEStreamStarted, &GUID_NULL, S_OK, &value); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + return S_OK; +} + +static HRESULT WINAPI test_source_Stop(IMFMediaSource *iface) +{ + return S_OK; +} + +static HRESULT WINAPI test_source_Pause(IMFMediaSource *iface) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI test_source_Shutdown(IMFMediaSource *iface) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static const IMFMediaSourceVtbl test_source_vtbl = +{ + test_source_QueryInterface, + test_source_AddRef, + test_source_Release, + test_source_GetEvent, + test_source_BeginGetEvent, + test_source_EndGetEvent, + test_source_QueueEvent, + test_source_GetCharacteristics, + test_source_CreatePresentationDescriptor, + test_source_Start, + test_source_Stop, + test_source_Pause, + test_source_Shutdown, +}; + +static IMFMediaType *create_media_type(const GUID *subtype, UINT32 width, UINT32 height) +{ + IMFMediaType *media_type; + HRESULT hr; + + hr = MFCreateMediaType(&media_type); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaType_SetGUID(media_type, &MF_MT_MAJOR_TYPE, &MFMediaType_Video); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaType_SetGUID(media_type, &MF_MT_SUBTYPE, subtype); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaType_SetUINT32(media_type, &MF_MT_COMPRESSED, FALSE); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaType_SetUINT32(media_type, &MF_MT_ALL_SAMPLES_INDEPENDENT, TRUE); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaType_SetUINT32(media_type, &MF_MT_INTERLACE_MODE, FALSE); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaType_SetUINT64(media_type, &MF_MT_FRAME_SIZE, ((UINT64)width) << 32 | height); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaType_SetUINT64(media_type, &MF_MT_FRAME_RATE, ((UINT64)30) << 32 | 1); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaType_SetUINT64(media_type, &MF_MT_PIXEL_ASPECT_RATIO, ((UINT64)1) << 32 | 1); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + return media_type; +} + +static struct test_source *create_test_source(const GUID *subtype, UINT32 width, UINT32 height) +{ + struct test_source *source; + HRESULT hr; + + source = malloc(sizeof(*source)); + ok(!!source, "Failed to allocate memory for source\n"); + source->IMFMediaSource_iface.lpVtbl = &test_source_vtbl; + source->IMFMediaStream_iface.lpVtbl = &test_stream_vtbl; + source->refcount = 1; + source->type = create_media_type(subtype, width, height); + hr = MFCreateStreamDescriptor(0, 1, &source->type, &source->sd); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = MFCreatePresentationDescriptor(1, &source->sd, &source->pd); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFPresentationDescriptor_SelectStream(source->pd, 0); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = MFCreateEventQueue(&source->media_queue); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = MFCreateEventQueue(&source->stream_queue); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + source->subtype = subtype; + source->width = width; + source->height = height; + return source; +} + +static IMFTopologyNode *create_source_node(const GUID *subtype, UINT32 width, UINT32 height) +{ + IMFPresentationDescriptor *pd; + struct test_source *source; + IMFTopologyNode *node; + IMFMediaType *type; + HRESULT hr; + + hr = MFCreateTopologyNode(MF_TOPOLOGY_SOURCESTREAM_NODE, &node); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + source = create_test_source(subtype, width, height); + hr = IMFPresentationDescriptor_Clone(source->pd, &pd); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + MFCreateMediaType(&type); + hr = IMFMediaType_CopyAllItems(source->type, (IMFAttributes*)type); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFTopologyNode_SetUnknown(node, &MF_TOPONODE_SOURCE, (IUnknown*)&source->IMFMediaSource_iface); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFTopologyNode_SetUnknown(node, &MF_TOPONODE_PRESENTATION_DESCRIPTOR, (IUnknown*)pd); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFTopologyNode_SetUnknown(node, &MF_TOPONODE_STREAM_DESCRIPTOR, (IUnknown*)source->sd); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFTopologyNode_SetOutputPrefType(node, 0, type); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFTopologyNode_SetUINT64(node, &MF_TOPONODE_MEDIASTART, 0); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + IMFPresentationDescriptor_Release(pd); + IMFMediaType_Release(type); + return node; +} + +struct test_presenter +{ + IMFVideoPresenter IMFVideoPresenter_iface; + IMFGetService IMFGetService_iface; + IMFTopologyServiceLookupClient IMFTopologyServiceLookupClient_iface; + IMFVideoDeviceID IMFVideoDeviceID_iface; + LONG refcount; + + IDirect3DDevice9 *d3d9device; + IDirect3DSwapChain9 *swapchain; + IDirect3DDeviceManager9 *device_manager; + IMFSample *sample; + IMediaEventSink *sink; + IMFTransform *transform; + IMFVideoMediaType *video_type; + UINT32 width; + UINT32 height; +}; + +static struct test_presenter* impl_from_IMFVideoPresenter(IMFVideoPresenter *iface) +{ + return CONTAINING_RECORD(iface, struct test_presenter, IMFVideoPresenter_iface); +} + +static struct test_presenter* impl_from_IMFGetService(IMFGetService *iface) +{ + return CONTAINING_RECORD(iface, struct test_presenter, IMFGetService_iface); +} + +static struct test_presenter* impl_from_IMFTopologyServiceLookupClient(IMFTopologyServiceLookupClient *iface) +{ + return CONTAINING_RECORD(iface, struct test_presenter, IMFTopologyServiceLookupClient_iface); +} + +static struct test_presenter* impl_from_presenter_IMFVideoDeviceID(IMFVideoDeviceID *iface) +{ + return CONTAINING_RECORD(iface, struct test_presenter, IMFVideoDeviceID_iface); +} + +static HRESULT WINAPI presenter_device_QueryInterface(IMFVideoDeviceID *iface, REFIID riid, void **obj) +{ + struct test_presenter *presenter = impl_from_presenter_IMFVideoDeviceID(iface); + return IMFVideoPresenter_QueryInterface(&presenter->IMFVideoPresenter_iface, riid, obj); +} + +static ULONG WINAPI presenter_device_AddRef(IMFVideoDeviceID *iface) +{ + struct test_presenter *presenter = impl_from_presenter_IMFVideoDeviceID(iface); + return IMFVideoPresenter_AddRef(&presenter->IMFVideoPresenter_iface); +} + +static ULONG WINAPI presenter_device_Release(IMFVideoDeviceID *iface) +{ + struct test_presenter *presenter = impl_from_presenter_IMFVideoDeviceID(iface); + return IMFVideoPresenter_Release(&presenter->IMFVideoPresenter_iface); +} + +static HRESULT WINAPI presenter_device_GetDeviceID(IMFVideoDeviceID *iface, IID *pDeviceID) +{ + *pDeviceID = IID_IDirect3DDevice9; + return S_OK; +} + +static IMFVideoDeviceIDVtbl presenter_device_vtbl = +{ + presenter_device_QueryInterface, + presenter_device_AddRef, + presenter_device_Release, + presenter_device_GetDeviceID, +}; + +static HRESULT WINAPI presenter_service_lookup_QueryInterface(IMFTopologyServiceLookupClient *iface, REFIID riid, void **obj) +{ + struct test_presenter *presenter = impl_from_IMFTopologyServiceLookupClient(iface); + return IMFVideoPresenter_QueryInterface(&presenter->IMFVideoPresenter_iface, riid, obj); +} + +static ULONG WINAPI presenter_service_lookup_AddRef(IMFTopologyServiceLookupClient *iface) +{ + struct test_presenter *presenter = impl_from_IMFTopologyServiceLookupClient(iface); + return IMFVideoPresenter_AddRef(&presenter->IMFVideoPresenter_iface); +} + +static ULONG WINAPI presenter_service_lookup_Release(IMFTopologyServiceLookupClient *iface) +{ + struct test_presenter *presenter = impl_from_IMFTopologyServiceLookupClient(iface); + return IMFVideoPresenter_Release(&presenter->IMFVideoPresenter_iface); +} + +static HRESULT WINAPI presenter_service_lookup_InitServicePointers(IMFTopologyServiceLookupClient *iface, IMFTopologyServiceLookup *pLookup) +{ + HRESULT hr; + struct test_presenter *presenter = impl_from_IMFTopologyServiceLookupClient(iface); + DWORD nObjects = 1; + + hr = IMFTopologyServiceLookup_LookupService(pLookup, MF_SERVICE_LOOKUP_GLOBAL, 0, &MR_VIDEO_RENDER_SERVICE, &IID_IMediaEventSink, (void**)&presenter->sink, &nObjects); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(nObjects == 1, "Unexepected nObjects %ld.\n", nObjects); + + nObjects = 1; + hr = IMFTopologyServiceLookup_LookupService(pLookup, MF_SERVICE_LOOKUP_GLOBAL, 0, &MR_VIDEO_MIXER_SERVICE, &IID_IMFTransform, (void**)&presenter->transform, &nObjects); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(nObjects == 1, "Unexepected nObjects %ld.\n", nObjects); + return hr; +} + +static HRESULT WINAPI presenter_service_lookup_ReleaseServicePointers(IMFTopologyServiceLookupClient *iface) +{ + struct test_presenter *presenter = impl_from_IMFTopologyServiceLookupClient(iface); + + if (presenter->sink) + { + IMediaEventSink_Release(presenter->sink); + presenter->sink = NULL; + } + if (presenter->transform) + { + IMFTransform_Release(presenter->transform); + presenter->transform = NULL; + } + if (presenter->video_type) + { + IMFVideoMediaType_Release(presenter->video_type); + presenter->video_type = NULL; + } + + return S_OK; +} + +static IMFTopologyServiceLookupClientVtbl presenter_service_lookup_vtbl = +{ + presenter_service_lookup_QueryInterface, + presenter_service_lookup_AddRef, + presenter_service_lookup_Release, + presenter_service_lookup_InitServicePointers, + presenter_service_lookup_ReleaseServicePointers, +}; + +static HRESULT WINAPI presenter_get_service_QueryInterface(IMFGetService *iface, REFIID riid, void **obj) +{ + struct test_presenter *presenter = impl_from_IMFGetService(iface); + return IMFVideoPresenter_QueryInterface(&presenter->IMFVideoPresenter_iface, riid, obj); +} + +static ULONG WINAPI presenter_get_service_AddRef(IMFGetService *iface) +{ + struct test_presenter *presenter = impl_from_IMFGetService(iface); + return IMFVideoPresenter_AddRef(&presenter->IMFVideoPresenter_iface); +} + +static ULONG WINAPI presenter_get_service_Release(IMFGetService *iface) +{ + struct test_presenter *presenter = impl_from_IMFGetService(iface); + return IMFVideoPresenter_Release(&presenter->IMFVideoPresenter_iface); +} + +static HRESULT WINAPI presenter_get_service_GetService(IMFGetService *iface, REFGUID guidService, REFIID riid, void **ppvObject) +{ + struct test_presenter *presenter = impl_from_IMFGetService(iface); + if (IsEqualGUID(guidService, &MR_VIDEO_RENDER_SERVICE)) + { + if (IsEqualGUID(riid, &IID_IDirect3DDeviceManager9)) + IDirect3DDeviceManager9_AddRef((IDirect3DDeviceManager9*)(*ppvObject = presenter->device_manager)); + else + return E_NOINTERFACE; + + return S_OK; + } + + return MF_E_UNSUPPORTED_SERVICE; +} + +static IMFGetServiceVtbl presenter_get_service_vtbl = +{ + presenter_get_service_QueryInterface, + presenter_get_service_AddRef, + presenter_get_service_Release, + presenter_get_service_GetService, +}; + +static HRESULT WINAPI test_presenter_QueryInterface(IMFVideoPresenter *iface, REFIID riid, void **obj) +{ + struct test_presenter *presenter = impl_from_IMFVideoPresenter(iface); + + if (IsEqualIID(riid, &IID_IMFVideoPresenter) || + IsEqualIID(riid, &IID_IUnknown)) + *obj = &presenter->IMFVideoPresenter_iface; + else if (IsEqualIID(riid, &IID_IMFGetService)) + *obj = &presenter->IMFGetService_iface; + else if (IsEqualIID(riid, &IID_IMFTopologyServiceLookupClient)) + *obj = &presenter->IMFTopologyServiceLookupClient_iface; + else if (IsEqualIID(riid, &IID_IMFVideoDeviceID)) + *obj = &presenter->IMFVideoDeviceID_iface; + else + { + *obj = NULL; + return E_NOINTERFACE; + } + + IMFVideoPresenter_AddRef(iface); + return S_OK; +} + +static ULONG WINAPI test_presenter_AddRef(IMFVideoPresenter *iface) +{ + struct test_presenter *presenter = impl_from_IMFVideoPresenter(iface); + return InterlockedIncrement(&presenter->refcount); +} + +static ULONG WINAPI test_presenter_Release(IMFVideoPresenter *iface) +{ + struct test_presenter *presenter = impl_from_IMFVideoPresenter(iface); + ULONG refcount = InterlockedDecrement(&presenter->refcount); + + if (!presenter->refcount) + { + IDirect3DDevice9_Release(presenter->d3d9device); + IDirect3DSwapChain9_Release(presenter->swapchain); + IDirect3DDeviceManager9_Release(presenter->device_manager); + if (presenter->sample) + { + IMFSample_Release(presenter->sample); + presenter->sample = NULL; + } + IMFTopologyServiceLookupClient_ReleaseServicePointers(&presenter->IMFTopologyServiceLookupClient_iface); + free(presenter); + } + + return refcount; +} + +static HRESULT WINAPI test_presenter_OnClockStart(IMFVideoPresenter *iface, MFTIME hnsSystemTime, LONGLONG llClockStartOffset) +{ + return S_OK; +} + +static HRESULT WINAPI test_presenter_OnClockStop(IMFVideoPresenter* iface, MFTIME hnsSystemTime) +{ + return S_OK; +} + +static HRESULT WINAPI test_presenter_OnClockPause(IMFVideoPresenter *iface, MFTIME hnsSystemTime) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI test_presenter_OnClockRestart(IMFVideoPresenter *iface, MFTIME hnsSystemTime) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI test_presenter_OnClockSetRate(IMFVideoPresenter *iface, MFTIME hnsSystemTime, float flRate) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI test_presenter_ProcessMessage(IMFVideoPresenter* iface, MFVP_MESSAGE_TYPE eMessage, ULONG_PTR ulParam) +{ + HRESULT hr; + struct test_presenter *presenter = impl_from_IMFVideoPresenter(iface); + + switch (eMessage) + { + case MFVP_MESSAGE_INVALIDATEMEDIATYPE: + { + IMFMediaType *type, *cloned_type; + GUID subtype; + + hr = IMFTransform_GetOutputAvailableType(presenter->transform, 0, 0, &type); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaType_GetGUID(type, &MF_MT_SUBTYPE, &subtype); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = MFCreateMediaType(&cloned_type); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaType_CopyAllItems(type, (IMFAttributes*)cloned_type); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaType_SetUINT32(cloned_type, &MF_MT_PAN_SCAN_ENABLED, FALSE); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaType_SetUINT64(cloned_type, &MF_MT_PIXEL_ASPECT_RATIO, (UINT64)1 << 32 | 1); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaType_SetUINT64(cloned_type, &MF_MT_FRAME_SIZE, (UINT64)presenter->width << 32 | presenter->height); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFTransform_SetOutputType(presenter->transform, 0, cloned_type, 0); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaType_QueryInterface(cloned_type, &IID_IMFVideoMediaType, (void**)&presenter->video_type); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + IMFMediaType_Release(cloned_type); + } + break; + + case MFVP_MESSAGE_PROCESSINPUTNOTIFY: + if (presenter->video_type) + { + DWORD status; + MFT_OUTPUT_DATA_BUFFER data_buffer = { + .dwStreamID = 0, + .pSample = presenter->sample, + .dwStatus = 0, + .pEvents = NULL, + }; + hr = IMFTransform_ProcessOutput(presenter->transform, 0, 1, &data_buffer, &status); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hit_set_again = TRUE; + break; + } + hit_not_set = TRUE; + return MF_E_TRANSFORM_TYPE_NOT_SET; + + + case MFVP_MESSAGE_ENDOFSTREAM: + IMediaEventSink_Notify(presenter->sink, EC_COMPLETE, S_OK, 0); + case MFVP_MESSAGE_ENDSTREAMING: + case MFVP_MESSAGE_BEGINSTREAMING: + break; + case MFVP_MESSAGE_FLUSH: + if (presenter->sample) + { + IMFSample_Release(presenter->sample); + presenter->sample = NULL; + } + break; + default: + ok(0, "Unexpected eMessage %d.\n", eMessage); + } + return S_OK; +} + +static HRESULT WINAPI test_presenter_GetCurrentMediaType(IMFVideoPresenter* iface, IMFVideoMediaType **ppMediaType) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static IMFVideoPresenterVtbl test_presenter_vtbl = +{ + test_presenter_QueryInterface, + test_presenter_AddRef, + test_presenter_Release, + test_presenter_OnClockStart, + test_presenter_OnClockStop, + test_presenter_OnClockPause, + test_presenter_OnClockRestart, + test_presenter_OnClockSetRate, + test_presenter_ProcessMessage, + test_presenter_GetCurrentMediaType, +}; + +static struct test_presenter *create_test_presenter(HWND hwnd, D3DFORMAT format, UINT32 width, UINT32 height) +{ + D3DPRESENT_PARAMETERS params = + { + .BackBufferWidth = width, + .BackBufferHeight = height, + .BackBufferFormat = format, + .BackBufferCount = 0, + .MultiSampleType = D3DMULTISAMPLE_NONE, + .MultiSampleQuality = 0, + .SwapEffect = D3DSWAPEFFECT_COPY, + .hDeviceWindow = hwnd, + .Windowed = TRUE, + .EnableAutoDepthStencil = TRUE, + .AutoDepthStencilFormat = D3DFMT_D16, + .Flags = D3DPRESENTFLAG_DEVICECLIP, + .FullScreen_RefreshRateInHz = 0, + .PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT, + }; + struct test_presenter *presenter; + IDirect3DSurface9 *surface; + IDirect3D9 *d3d9; + UINT resetToken; + HRESULT hr; + + presenter = malloc(sizeof(*presenter)); + ok(!!presenter, "Failed to allocate memory for presenter\n"); + presenter->IMFVideoPresenter_iface.lpVtbl = &test_presenter_vtbl; + presenter->IMFGetService_iface.lpVtbl = &presenter_get_service_vtbl; + presenter->IMFTopologyServiceLookupClient_iface.lpVtbl = &presenter_service_lookup_vtbl; + presenter->IMFVideoDeviceID_iface.lpVtbl = &presenter_device_vtbl; + presenter->refcount = 1; + + d3d9 = Direct3DCreate9(D3D_SDK_VERSION); + ok(!!d3d9, "Failed to create d3d9\n"); + hr = IDirect3D9_CreateDevice(d3d9, D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hwnd, D3DCREATE_MULTITHREADED | D3DCREATE_HARDWARE_VERTEXPROCESSING, ¶ms, &presenter->d3d9device); + ok(hr == S_OK, "Failed to create d3d9 device hr %#lx.\n", hr); + IDirect3D9_Release(d3d9); + hr = DXVA2CreateDirect3DDeviceManager9(&resetToken, &presenter->device_manager); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IDirect3DDeviceManager9_ResetDevice(presenter->device_manager, presenter->d3d9device, resetToken); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IDirect3DDevice9_CreateAdditionalSwapChain(presenter->d3d9device, ¶ms, &presenter->swapchain); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IDirect3DSwapChain9_GetBackBuffer(presenter->swapchain, 0, D3DBACKBUFFER_TYPE_MONO, &surface); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = MFCreateVideoSampleFromSurface((IUnknown*)surface, &presenter->sample); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + IDirect3DSurface9_Release(surface); + + presenter->transform = NULL; + presenter->video_type = NULL; + + presenter->width = width; + presenter->height = height; + return presenter; +} + +struct activate_presenter +{ + IMFActivate IMFActivate_iface; + LONG refcount; + + struct test_presenter *presenter; + HWND hwnd; + D3DFORMAT format; + UINT32 width; + UINT32 height; +}; + +static struct activate_presenter* impl_from_presenter_IMFActivate(IMFActivate *iface) +{ + return CONTAINING_RECORD(iface, struct activate_presenter, IMFActivate_iface); +} + +static HRESULT WINAPI activate_presenter_QueryInterface(IMFActivate *iface, REFIID riid, void **obj) +{ + if (IsEqualIID(riid, &IID_IMFActivate) || + IsEqualIID(riid, &IID_IUnknown)) + { + *obj = iface; + } + else + { + *obj = NULL; + return E_NOINTERFACE; + } + + IMFActivate_AddRef(iface); + return S_OK; +} + +static ULONG WINAPI activate_presenter_AddRef(IMFActivate *iface) +{ + struct activate_presenter *activate = impl_from_presenter_IMFActivate(iface); + return InterlockedIncrement(&activate->refcount); +} + +static ULONG WINAPI activate_presenter_Release(IMFActivate *iface) +{ + struct activate_presenter *activate = impl_from_presenter_IMFActivate(iface); + ULONG refcount = InterlockedDecrement(&activate->refcount); + + if (!activate->refcount) + { + if (activate->presenter) + IMFVideoPresenter_Release(&activate->presenter->IMFVideoPresenter_iface); + free(activate); + } + + return refcount; +} + +static HRESULT WINAPI activate_presenter_GetItem(IMFActivate *iface, REFGUID guidKey, PROPVARIANT *pValue) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI activate_presenter_GetItemType(IMFActivate *iface, REFGUID guidKey, MF_ATTRIBUTE_TYPE *pType) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI activate_presenter_CompareItem(IMFActivate *iface, REFGUID guidKey, REFPROPVARIANT Value, BOOL *pbResult) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI activate_presenter_Compare(IMFActivate *iface, IMFAttributes *pTheirs, MF_ATTRIBUTES_MATCH_TYPE MatchType, BOOL *pbResult) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI activate_presenter_GetUINT32(IMFActivate *iface, REFGUID guidKey, UINT32 *punValue) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI activate_presenter_GetUINT64(IMFActivate *iface, REFGUID guidKey, UINT64 *punValue) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI activate_presenter_GetDouble(IMFActivate *iface, REFGUID guidKey, double *pfValue) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI activate_presenter_GetGUID(IMFActivate *iface, REFGUID guidKey, GUID *pguidValue) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI activate_presenter_GetStringLength(IMFActivate *iface, REFGUID guidKey, UINT32 *pcchLength) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI activate_presenter_GetString(IMFActivate *iface, REFGUID guidKey, LPWSTR pwszValue, UINT32 cchBufSize, UINT32 *pcchLength) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI activate_presenter_GetAllocatedString(IMFActivate *iface, REFGUID guidKey, LPWSTR *ppwszValue, UINT32 *pcchLength) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI activate_presenter_GetBlobSize(IMFActivate *iface, REFGUID guidKey, UINT32 *pcbBlobSize) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI activate_presenter_GetBlob(IMFActivate *iface, REFGUID guidKey, UINT8 *pBuf, UINT32 cbBufSize, UINT32 *pcbBlobSize) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI activate_presenter_GetAllocatedBlob(IMFActivate *iface, REFGUID guidKey, UINT8 **ppBuf, UINT32 *pcbSize) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI activate_presenter_GetUnknown(IMFActivate *iface, REFGUID guidKey, REFIID riid, LPVOID *ppv) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI activate_presenter_SetItem(IMFActivate *iface, REFGUID guidKey, REFPROPVARIANT Value) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI activate_presenter_DeleteItem(IMFActivate *iface, REFGUID guidKey) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI activate_presenter_DeleteAllItems(IMFActivate *iface) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI activate_presenter_SetUINT32(IMFActivate *iface, REFGUID guidKey, UINT32 unValue) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI activate_presenter_SetUINT64(IMFActivate *iface, REFGUID guidKey, UINT64 unValue) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI activate_presenter_SetDouble(IMFActivate *iface, REFGUID guidKey, double fValue) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI activate_presenter_SetGUID(IMFActivate *iface, REFGUID guidKey, REFGUID guidValue) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI activate_presenter_SetString(IMFActivate *iface, REFGUID guidKey, LPCWSTR wszValue) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI activate_presenter_SetBlob(IMFActivate *iface, REFGUID guidKey, const UINT8 *pBuf, UINT32 cbBufSize) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI activate_presenter_SetUnknown(IMFActivate *iface, REFGUID guidKey, IUnknown *pUnknown) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI activate_presenter_LockStore(IMFActivate *iface) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI activate_presenter_UnlockStore(IMFActivate *iface) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI activate_presenter_GetCount(IMFActivate *iface, UINT32 *pcItems) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI activate_presenter_GetItemByIndex(IMFActivate *iface, UINT32 unIndex, GUID *pguidKey, PROPVARIANT *pValue) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI activate_presenter_CopyAllItems(IMFActivate *iface, IMFAttributes *pDest) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI activate_presenter_ActivateObject(IMFActivate *iface, REFIID riid, void **ppv) +{ + struct activate_presenter *activate = impl_from_presenter_IMFActivate(iface); + if (!activate->presenter) + activate->presenter = create_test_presenter(activate->hwnd, activate->format, activate->width, activate->height); + + if (activate->presenter) + return IMFVideoPresenter_QueryInterface(&activate->presenter->IMFVideoPresenter_iface, riid, ppv); + + return MF_E_NOT_INITIALIZED; +} + +static HRESULT WINAPI activate_presenter_DetachObject(IMFActivate *iface) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI activate_presenter_ShutdownObject(IMFActivate *iface) +{ + struct activate_presenter *activate = impl_from_presenter_IMFActivate(iface); + if (activate->presenter) + { + IMFVideoPresenter_Release(&activate->presenter->IMFVideoPresenter_iface); + activate->presenter = NULL; + } + + return S_OK; +} + +static IMFActivateVtbl activate_presenter_vtbl = +{ + activate_presenter_QueryInterface, + activate_presenter_AddRef, + activate_presenter_Release, + activate_presenter_GetItem, + activate_presenter_GetItemType, + activate_presenter_CompareItem, + activate_presenter_Compare, + activate_presenter_GetUINT32, + activate_presenter_GetUINT64, + activate_presenter_GetDouble, + activate_presenter_GetGUID, + activate_presenter_GetStringLength, + activate_presenter_GetString, + activate_presenter_GetAllocatedString, + activate_presenter_GetBlobSize, + activate_presenter_GetBlob, + activate_presenter_GetAllocatedBlob, + activate_presenter_GetUnknown, + activate_presenter_SetItem, + activate_presenter_DeleteItem, + activate_presenter_DeleteAllItems, + activate_presenter_SetUINT32, + activate_presenter_SetUINT64, + activate_presenter_SetDouble, + activate_presenter_SetGUID, + activate_presenter_SetString, + activate_presenter_SetBlob, + activate_presenter_SetUnknown, + activate_presenter_LockStore, + activate_presenter_UnlockStore, + activate_presenter_GetCount, + activate_presenter_GetItemByIndex, + activate_presenter_CopyAllItems, + activate_presenter_ActivateObject, + activate_presenter_DetachObject, + activate_presenter_ShutdownObject, +}; + +struct activate_presenter *create_activate_presenter(HWND hwnd, D3DFORMAT format, UINT32 width, UINT32 height) +{ + struct activate_presenter *activate = malloc(sizeof(*activate)); + ok(!!activate, "Failed to allocate memory for activate\n"); + activate->IMFActivate_iface.lpVtbl = &activate_presenter_vtbl; + activate->refcount = 1; + activate->hwnd = hwnd; + activate->presenter = NULL; + activate->format = format; + activate->width = width; + activate->height = height; + return activate; +} + +static IMFTopologyNode *create_sink_node(HWND hwnd, D3DFORMAT format, UINT32 width, UINT32 height) +{ + struct activate_presenter *presenter; + IMFActivate *renderer; + IMFTopologyNode *node; + HRESULT hr; + + hr = MFCreateVideoRendererActivate(NULL, &renderer); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + presenter = create_activate_presenter(hwnd, format, width, height); + hr = IMFActivate_SetUnknown(renderer, &MF_ACTIVATE_CUSTOM_VIDEO_PRESENTER_ACTIVATE, (IUnknown*)&presenter->IMFActivate_iface); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = MFCreateTopologyNode(MF_TOPOLOGY_OUTPUT_NODE, &node); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFTopologyNode_SetObject(node, (IUnknown*)renderer); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + IMFActivate_Release(renderer); + return node; +} + +static void test_custom_processor(void) +{ + const GUID *subtype = &MFVideoFormat_RGB32; + const UINT32 width = 100, height = 100; + IMFTopologyNode *source_node, *sink_node; + IMFAsyncCallback* event_callback; + IMFMediaSession *session; + IMFTopology *topology; + IMFMediaEvent *event; + UINT32 topo_status; + PROPVARIANT empty; + HRESULT hr; + HWND hwnd; + + hr = MFStartup(MF_VERSION, 0); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = CoInitialize(NULL); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hwnd = create_window(); + + hr = MFCreateMediaSession(NULL, &session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = MFCreateTopology(&topology); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + source_node = create_source_node(&MFVideoFormat_RGB32, width, height); + hr = IMFTopology_AddNode(topology, source_node); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + sink_node = create_sink_node(hwnd, subtype->Data1, width, height); + hr = IMFTopologyNode_SetUINT32(sink_node, &MF_TOPONODE_CONNECT_METHOD, MF_CONNECT_DIRECT); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFTopology_AddNode(topology, sink_node); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFTopologyNode_ConnectOutput(source_node, 0, sink_node, 0); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + IMFTopologyNode_Release(source_node); + IMFTopologyNode_Release(sink_node); + hr = IMFMediaSession_SetTopology(session, 0, topology); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + IMFTopology_Release(topology); + + PropVariantInit(&empty); + event_callback = create_event_callback(TRUE); + + /* wait for and handle MF_TOPOSTATUS_READY */ + hr = wait_media_event_until_blocking(session, event_callback, MESessionTopologyStatus, 1000, NULL, &event); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaEvent_GetUINT32(event, &MF_EVENT_TOPOLOGY_STATUS, &topo_status); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(topo_status == MF_TOPOSTATUS_READY, "Unexpected topology status %d\n", topo_status); + hr = IMFMediaSession_Start(session, NULL, &empty); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + IMFMediaEvent_Release(event); + + /* wait for and handle MF_TOPOSTATUS_STARTED_SOURCE */ + hr = wait_media_event_until_blocking(session, event_callback, MESessionTopologyStatus, 1000, NULL, &event); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaEvent_GetUINT32(event, &MF_EVENT_TOPOLOGY_STATUS, &topo_status); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(topo_status == MF_TOPOSTATUS_STARTED_SOURCE, "Unexpected topology status %d\n", topo_status); + IMFMediaEvent_Release(event); + + /* wait for and handle MF_TOPOSTATUS_ENDED */ + hr = wait_media_event_until_blocking(session, event_callback, MESessionTopologyStatus, 1000, NULL, &event); + todo_wine + ok(hr == S_OK || broken(!hit_not_set), "Unexpected hr %#lx.\n", hr); /* Win 8 doesn't send MFVP_MESSAGE_PROCESSINPUTNOTIFY */ + hr = IMFMediaEvent_GetUINT32(event, &MF_EVENT_TOPOLOGY_STATUS, &topo_status); + todo_wine + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + todo_wine + /* Win 8 doesn't send MFVP_MESSAGE_PROCESSINPUTNOTIFY */ + ok(topo_status == MF_TOPOSTATUS_ENDED || broken(!hit_not_set), "Unexpected topology status %d\n", topo_status); + IMFMediaEvent_Release(event); + + ok(hit_not_set || broken(!hit_not_set), "Should have un-set the media type\n"); /* Win 8 doesn't send MFVP_MESSAGE_PROCESSINPUTNOTIFY */ + todo_wine + ok(hit_set_again || broken(!hit_not_set), "Should have set the media type again\n"); /* Win 8 doesn't send MFVP_MESSAGE_PROCESSINPUTNOTIFY */ + + hr = IMFMediaSession_Shutdown(session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + IMFMediaSession_Release(session); + + MFShutdown(); + CoUninitialize(); +} + +START_TEST(evr) +{ + init_functions(); + + test_custom_processor(); +}