Send INVALIDATEMEDIATYPE to allow the transform type to be set before retrying PROCESSINPUTNOTIFY.
-- v3: mf: Retry PROCESSINPUTNOTIFY if TRANSFORM_TYPE_NOT_SET is returned. mf/tests: Add tests for custom evr presenter.
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(); +}
From: Brendan McGrath bmcgrath@codeweavers.com
Send INVALIDATEMEDIATYPE to allow the transform type to be set before retrying PROCESSINPUTNOTIFY. --- dlls/mf/evr.c | 8 +++++++- dlls/mf/tests/evr.c | 4 ---- 2 files changed, 7 insertions(+), 5 deletions(-)
diff --git a/dlls/mf/evr.c b/dlls/mf/evr.c index db7053a1405..2f7e7e8b997 100644 --- a/dlls/mf/evr.c +++ b/dlls/mf/evr.c @@ -416,7 +416,13 @@ static HRESULT WINAPI video_stream_sink_ProcessSample(IMFStreamSink *iface, IMFS }
if (SUCCEEDED(IMFTransform_ProcessInput(stream->parent->mixer, stream->id, sample, 0))) - IMFVideoPresenter_ProcessMessage(stream->parent->presenter, MFVP_MESSAGE_PROCESSINPUTNOTIFY, 0); + { + if (IMFVideoPresenter_ProcessMessage(stream->parent->presenter, MFVP_MESSAGE_PROCESSINPUTNOTIFY, 0) == MF_E_TRANSFORM_TYPE_NOT_SET) + { + IMFVideoPresenter_ProcessMessage(stream->parent->presenter, MFVP_MESSAGE_INVALIDATEMEDIATYPE, 0); + IMFVideoPresenter_ProcessMessage(stream->parent->presenter, MFVP_MESSAGE_PROCESSINPUTNOTIFY, 0); + } + }
if (stream->flags & EVR_STREAM_PREROLLING) { diff --git a/dlls/mf/tests/evr.c b/dlls/mf/tests/evr.c index fa4e6d3beb4..e3a4a579e72 100644 --- a/dlls/mf/tests/evr.c +++ b/dlls/mf/tests/evr.c @@ -1523,18 +1523,14 @@ static void test_custom_processor(void)
/* 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);
Nikolay Sivov (@nsivov) commented about dlls/mf/evr.c:
} if (SUCCEEDED(IMFTransform_ProcessInput(stream->parent->mixer, stream->id, sample, 0)))
IMFVideoPresenter_ProcessMessage(stream->parent->presenter, MFVP_MESSAGE_PROCESSINPUTNOTIFY, 0);
{
if (IMFVideoPresenter_ProcessMessage(stream->parent->presenter, MFVP_MESSAGE_PROCESSINPUTNOTIFY, 0) == MF_E_TRANSFORM_TYPE_NOT_SET)
{
IMFVideoPresenter_ProcessMessage(stream->parent->presenter, MFVP_MESSAGE_INVALIDATEMEDIATYPE, 0);
IMFVideoPresenter_ProcessMessage(stream->parent->presenter, MFVP_MESSAGE_PROCESSINPUTNOTIFY, 0);
}
}
It's not clear what it should do if MFVP_MESSAGE_PROCESSINPUTNOTIFY keeps failing, of if MFVP_MESSAGE_INVALIDATEMEDIATYPE fails. Regarding the tests, the main problem is that such tests quickly become unmanageable, once you add some other paths to test different situation. Also it's covered with broken() checks all over. It might be better to figure out first if we need to retry more than once, and then strip the tests and attach them as a plain diff for this MR.