From 214f23c8fe4f5a5ed4b985d77c49a30e079217ff Mon Sep 17 00:00:00 2001 From: Maarten Lankhorst Date: Mon, 28 Jul 2008 18:18:06 -0700 Subject: [PATCH] quartz: Add tests for Video Mixing Renderer 9 --- dlls/quartz/tests/Makefile.in | 5 +- dlls/quartz/tests/vmr9.c | 1522 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 1525 insertions(+), 2 deletions(-) create mode 100644 dlls/quartz/tests/vmr9.c diff --git a/dlls/quartz/tests/Makefile.in b/dlls/quartz/tests/Makefile.in index d21f281..6ce95e0 100644 --- a/dlls/quartz/tests/Makefile.in +++ b/dlls/quartz/tests/Makefile.in @@ -3,7 +3,7 @@ TOPOBJDIR = ../../.. SRCDIR = @srcdir@ VPATH = @srcdir@ TESTDLL = quartz.dll -IMPORTS = oleaut32 ole32 advapi32 kernel32 +IMPORTS = oleaut32 ole32 advapi32 kernel32 user32 gdi32 CTESTS = \ avisplitter.c \ @@ -12,7 +12,8 @@ CTESTS = \ memallocator.c \ misc.c \ referenceclock.c \ - videorenderer.c + videorenderer.c \ + vmr9.c @MAKE_TEST_RULES@ diff --git a/dlls/quartz/tests/vmr9.c b/dlls/quartz/tests/vmr9.c new file mode 100644 index 0000000..8652767 --- /dev/null +++ b/dlls/quartz/tests/vmr9.c @@ -0,0 +1,1522 @@ +/* + * Unit tests for Video Renderer Mixer functions + * + * Copyright (C) 2004 Christian Costa + * Copyright (C) 2007 Google (Lei Zhang) + * Copyright (C) 2008 Google (Maarten Lankhorst) + * + * 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 +#define NONAMELESSUNION + +#include "wine/test.h" +#include "dshow.h" +#include "strmif.h" +#include "d3d9.h" +#include "initguid.h" +#include "vmr9.h" +#include "dvdmedia.h" +#include "assert.h" + +static const WCHAR wcsNullPinName[] = {'N','u','l','l',' ','p','i','n',0}; + +static const IBaseFilterVtbl NullWriter_Vtbl; +static const IPinVtbl NullWriterPin_Vtbl; +static const IEnumMediaTypesVtbl NullWriterEnum_Vtbl; + +#define ICOM_THIS_MULTI(impl,field,iface) impl* const This=(impl*)((char*)(iface) - offsetof(impl,field)) + +typedef struct NullWriterImpl +{ + const IBaseFilterVtbl * lpVtbl; + + LONG refCount; + CRITICAL_SECTION csFilter; + FILTER_STATE state; + REFERENCE_TIME rtStreamStart; + IReferenceClock * pClock; + FILTER_INFO filterInfo; + + const IPinVtbl *outputpin; + IPin *pConnectedTo; + + AM_MEDIA_TYPE amt; + + const IEnumMediaTypesVtbl *enummt; + ULONG pos; +} NullWriterImpl; + +HRESULT NullWriter_create(IBaseFilter **ppv, IPin **pin) +{ + HRESULT hr = S_OK; + NullWriterImpl * pNullWriter; + + + *ppv = NULL; + *pin = NULL; + + pNullWriter = CoTaskMemAlloc(sizeof(NullWriterImpl)); + pNullWriter->lpVtbl = &NullWriter_Vtbl; + pNullWriter->refCount = 2; + InitializeCriticalSection(&pNullWriter->csFilter); + pNullWriter->csFilter.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": NullWriterImpl.csFilter"); + pNullWriter->state = State_Stopped; + pNullWriter->pClock = NULL; + pNullWriter->outputpin = &NullWriterPin_Vtbl; + pNullWriter->pConnectedTo = NULL; + pNullWriter->pos = 0; + pNullWriter->enummt = &NullWriterEnum_Vtbl; + ZeroMemory(&pNullWriter->filterInfo, sizeof(FILTER_INFO)); + + if (SUCCEEDED(hr)) + { + *ppv = (IBaseFilter *)pNullWriter; + *pin = (IPin *)&pNullWriter->outputpin; + } + else + { + pNullWriter->csFilter.DebugInfo->Spare[0] = 0; + DeleteCriticalSection(&pNullWriter->csFilter); + CoTaskMemFree(pNullWriter); + } + + return hr; +} + +static HRESULT WINAPI NullWriter_QueryInterface(IBaseFilter * iface, REFIID riid, LPVOID * ppv) +{ + NullWriterImpl *This = (NullWriterImpl *)iface; + + *ppv = NULL; + if (IsEqualIID(riid, &IID_IUnknown)) + *ppv = (LPVOID)This; + else if (IsEqualIID(riid, &IID_IPersist)) + *ppv = (LPVOID)This; + else if (IsEqualIID(riid, &IID_IMediaFilter)) + *ppv = (LPVOID)This; + else if (IsEqualIID(riid, &IID_IBaseFilter)) + *ppv = (LPVOID)This; + + if (*ppv) + { + IUnknown_AddRef((IUnknown *)(*ppv)); + return S_OK; + } + + return E_NOINTERFACE; +} + +static ULONG WINAPI NullWriter_AddRef(IBaseFilter * iface) +{ + NullWriterImpl *This = (NullWriterImpl *)iface; + LONG ret = InterlockedIncrement(&This->refCount); + + + return ret; +} + +static ULONG WINAPI NullWriter_Release(IBaseFilter * iface) +{ + NullWriterImpl *This = (NullWriterImpl *)iface; + LONG ret = InterlockedDecrement(&This->refCount); + + + if (!ret) + { + + if (This->pClock) + IReferenceClock_Release(This->pClock); + + ok(!This->pConnectedTo, "Somehow still connected to something!\n"); + + This->lpVtbl = NULL; + + This->csFilter.DebugInfo->Spare[0] = 0; + DeleteCriticalSection(&This->csFilter); + + CoTaskMemFree(This); + } + return ret; +} + +/** IPersist methods **/ + +static HRESULT WINAPI NullWriter_GetClassID(IBaseFilter * iface, CLSID * pClsid) +{ + + *pClsid = CLSID_NullRenderer; + + return S_OK; +} + +/** IMediaFilter methods **/ + +static HRESULT WINAPI NullWriter_Stop(IBaseFilter * iface) +{ + NullWriterImpl *This = (NullWriterImpl *)iface; + + + EnterCriticalSection(&This->csFilter); + { + This->state = State_Stopped; + } + LeaveCriticalSection(&This->csFilter); + + return S_OK; +} + +static HRESULT WINAPI NullWriter_Pause(IBaseFilter * iface) +{ + NullWriterImpl *This = (NullWriterImpl *)iface; + + + EnterCriticalSection(&This->csFilter); + { + This->state = State_Paused; + } + LeaveCriticalSection(&This->csFilter); + + return S_OK; +} + +static HRESULT WINAPI NullWriter_Run(IBaseFilter * iface, REFERENCE_TIME tStart) +{ + NullWriterImpl *This = (NullWriterImpl *)iface; + + + EnterCriticalSection(&This->csFilter); + { + This->state = State_Running; + } + LeaveCriticalSection(&This->csFilter); + + return S_OK; +} + +static HRESULT WINAPI NullWriter_GetState(IBaseFilter * iface, DWORD dwMilliSecsTimeout, FILTER_STATE *pState) +{ + NullWriterImpl *This = (NullWriterImpl *)iface; + + + EnterCriticalSection(&This->csFilter); + { + *pState = This->state; + } + LeaveCriticalSection(&This->csFilter); + + return S_OK; +} + +static HRESULT WINAPI NullWriter_SetSyncSource(IBaseFilter * iface, IReferenceClock *pClock) +{ + NullWriterImpl *This = (NullWriterImpl *)iface; + + + EnterCriticalSection(&This->csFilter); + { + if (This->pClock) + IReferenceClock_Release(This->pClock); + This->pClock = pClock; + if (This->pClock) + IReferenceClock_AddRef(This->pClock); + } + LeaveCriticalSection(&This->csFilter); + + return S_OK; +} + +static HRESULT WINAPI NullWriter_GetSyncSource(IBaseFilter * iface, IReferenceClock **ppClock) +{ + NullWriterImpl *This = (NullWriterImpl *)iface; + + + EnterCriticalSection(&This->csFilter); + { + *ppClock = This->pClock; + if (This->pClock) + IReferenceClock_AddRef(This->pClock); + } + LeaveCriticalSection(&This->csFilter); + + return S_OK; +} + +/** IBaseFilter implementation **/ + +static HRESULT WINAPI NullWriter_EnumPins(IBaseFilter * iface, IEnumPins **ppEnum) +{ + + return E_NOTIMPL; +} + +static HRESULT WINAPI NullWriter_FindPin(IBaseFilter * iface, LPCWSTR Id, IPin **ppPin) +{ + + /* FIXME: critical section */ + + return E_NOTIMPL; +} + +static HRESULT WINAPI NullWriter_QueryFilterInfo(IBaseFilter * iface, FILTER_INFO *pInfo) +{ + NullWriterImpl *This = (NullWriterImpl *)iface; + + + lstrcpyW(pInfo->achName, This->filterInfo.achName); + pInfo->pGraph = This->filterInfo.pGraph; + + if (pInfo->pGraph) + IFilterGraph_AddRef(pInfo->pGraph); + + return S_OK; +} + +static HRESULT WINAPI NullWriter_JoinFilterGraph(IBaseFilter * iface, IFilterGraph *pGraph, LPCWSTR pName) +{ + NullWriterImpl *This = (NullWriterImpl *)iface; + + + EnterCriticalSection(&This->csFilter); + { + if (pName) + lstrcpyW(This->filterInfo.achName, pName); + else + *This->filterInfo.achName = '\0'; + This->filterInfo.pGraph = pGraph; + } + LeaveCriticalSection(&This->csFilter); + + return S_OK; +} + +static HRESULT WINAPI NullWriter_QueryVendorInfo(IBaseFilter * iface, LPWSTR *pVendorInfo) +{ + return E_NOTIMPL; +} + +static const IBaseFilterVtbl NullWriter_Vtbl = +{ + NullWriter_QueryInterface, + NullWriter_AddRef, + NullWriter_Release, + NullWriter_GetClassID, + NullWriter_Stop, + NullWriter_Pause, + NullWriter_Run, + NullWriter_GetState, + NullWriter_SetSyncSource, + NullWriter_GetSyncSource, + NullWriter_EnumPins, + NullWriter_FindPin, + NullWriter_QueryFilterInfo, + NullWriter_JoinFilterGraph, + NullWriter_QueryVendorInfo +}; + +static HRESULT WINAPI NullWriterPin_QueryInterface(IPin * iface, REFIID riid, LPVOID * ppv) +{ + + *ppv = NULL; + if (IsEqualIID(riid, &IID_IUnknown)) + *ppv = (LPVOID)iface; + else if (IsEqualIID(riid, &IID_IPin)) + *ppv = (LPVOID)iface; + + if (*ppv) + { + IUnknown_AddRef((IUnknown *)(*ppv)); + return S_OK; + } + return E_NOINTERFACE; +} + +static ULONG WINAPI NullWriterPin_AddRef(IPin * iface) +{ + ICOM_THIS_MULTI(NullWriterImpl, outputpin, iface); + return IBaseFilter_AddRef((IBaseFilter *)This);; +} + +static ULONG WINAPI NullWriterPin_Release(IPin * iface) +{ + ICOM_THIS_MULTI(NullWriterImpl, outputpin, iface); + return IBaseFilter_Release((IBaseFilter *)This); +} + +static HRESULT WINAPI NullWriterPin_Disconnect(IPin * iface) +{ + HRESULT hr; + ICOM_THIS_MULTI(NullWriterImpl, outputpin, iface); + + + EnterCriticalSection(&This->csFilter); + { + if (This->pConnectedTo) + { + IPin_Release(This->pConnectedTo); + This->pConnectedTo = NULL; + hr = S_OK; + } + else + hr = S_FALSE; + } + LeaveCriticalSection(&This->csFilter); + return hr; +} + +static HRESULT WINAPI NullWriterPin_ConnectedTo(IPin * iface, IPin ** ppPin) +{ + HRESULT hr; + ICOM_THIS_MULTI(NullWriterImpl, outputpin, iface); + + + EnterCriticalSection(&This->csFilter); + { + if (This->pConnectedTo) + { + *ppPin = This->pConnectedTo; + IPin_AddRef(*ppPin); + hr = S_OK; + } + else + hr = VFW_E_NOT_CONNECTED; + } + LeaveCriticalSection(&This->csFilter); + + return hr; +} + +static HRESULT WINAPI NullWriterPin_ConnectionMediaType(IPin * iface, AM_MEDIA_TYPE * pmt) +{ + HRESULT hr; + ICOM_THIS_MULTI(NullWriterImpl, outputpin, iface); + + + EnterCriticalSection(&This->csFilter); + { + if (This->pConnectedTo) + { + hr = IPin_ConnectionMediaType(This->pConnectedTo, pmt); + } + else + { + ZeroMemory(pmt, sizeof(*pmt)); + hr = VFW_E_NOT_CONNECTED; + } + } + LeaveCriticalSection(&This->csFilter); + + return hr; +} + +static HRESULT WINAPI NullWriterPin_QueryPinInfo(IPin * iface, PIN_INFO * pInfo) +{ + ICOM_THIS_MULTI(NullWriterImpl, outputpin, iface); + + + pInfo->pFilter = (IBaseFilter *)This; + IBaseFilter_AddRef((IBaseFilter *)This); + pInfo->dir = PINDIR_OUTPUT; + lstrcpynW(pInfo->achName, wcsNullPinName, MAX_PIN_NAME); + return S_OK; +} + +static HRESULT WINAPI NullWriterPin_QueryDirection(IPin * iface, PIN_DIRECTION * pPinDir) +{ + + *pPinDir = PINDIR_OUTPUT; + + return S_OK; +} + +static HRESULT WINAPI NullWriterPin_QueryId(IPin * iface, LPWSTR * Id) +{ + + *Id = CoTaskMemAlloc((lstrlenW(wcsNullPinName) + 1) * sizeof(WCHAR)); + if (!*Id) + return E_OUTOFMEMORY; + + lstrcpyW(*Id, wcsNullPinName); + + return S_OK; +} + +static HRESULT WINAPI NullWriterPin_QueryAccept(IPin * iface, const AM_MEDIA_TYPE * pmt) +{ + + return S_OK; +} + +static HRESULT WINAPI NullWriterPin_EnumMediaTypes(IPin * iface, IEnumMediaTypes ** ppEnum) +{ + ICOM_THIS_MULTI(NullWriterImpl, outputpin, iface); + + *ppEnum = (IEnumMediaTypes *)&This->enummt; + IEnumMediaTypes_AddRef(*ppEnum); + + return S_OK; +} + +static HRESULT WINAPI NullWriterPin_QueryInternalConnections(IPin * iface, IPin ** apPin, ULONG * cPin) +{ + + return E_NOTIMPL; +} + +static HRESULT WINAPI NullWriterPin_Connect(IPin * iface, IPin * pConnector, const AM_MEDIA_TYPE * pmt) +{ + ICOM_THIS_MULTI(NullWriterImpl, outputpin, iface); + HRESULT hr; + + This->amt = *pmt; + hr = IPin_ReceiveConnection(pConnector, iface, pmt); + if (hr == S_OK) + { + IPin_AddRef(pConnector); + This->pConnectedTo = pConnector; + } + return hr; +} + +static HRESULT WINAPI NullWriterPin_ReceiveConnection(IPin * iface, IPin * pReceivePin, const AM_MEDIA_TYPE * pmt) +{ + ok(0, "Unexpected call!\n"); + return E_UNEXPECTED; +} + +static HRESULT WINAPI NullWriterPin_EndOfStream(IPin * iface) +{ + ok(0, "Unexpected call!\n"); + return E_UNEXPECTED; +} + +static HRESULT WINAPI NullWriterPin_BeginFlush(IPin * iface) +{ + ok(0, "Unexpected call!\n"); + return E_UNEXPECTED; +} + +static HRESULT WINAPI NullWriterPin_EndFlush(IPin * iface) +{ + ok(0, "Unexpected call!\n"); + return E_UNEXPECTED; +} + +static HRESULT WINAPI NullWriterPin_NewSegment(IPin * iface, REFERENCE_TIME tStart, REFERENCE_TIME tStop, double dRate) +{ + ok(0, "Unexpected call!\n"); + return E_UNEXPECTED; +} + +static const IPinVtbl NullWriterPin_Vtbl = +{ + NullWriterPin_QueryInterface, + NullWriterPin_AddRef, + NullWriterPin_Release, + NullWriterPin_Connect, + NullWriterPin_ReceiveConnection, + NullWriterPin_Disconnect, + NullWriterPin_ConnectedTo, + NullWriterPin_ConnectionMediaType, + NullWriterPin_QueryPinInfo, + NullWriterPin_QueryDirection, + NullWriterPin_QueryId, + NullWriterPin_QueryAccept, + NullWriterPin_EnumMediaTypes, + NullWriterPin_QueryInternalConnections, + NullWriterPin_EndOfStream, + NullWriterPin_BeginFlush, + NullWriterPin_EndFlush, + NullWriterPin_NewSegment +}; + +static HRESULT WINAPI NullWriterEnum_QueryInterface(IEnumMediaTypes * iface, REFIID riid, LPVOID * ppv) +{ + + *ppv = NULL; + if (IsEqualIID(riid, &IID_IUnknown)) + *ppv = (LPVOID)iface; + else if (IsEqualIID(riid, &IID_IEnumMediaTypes)) + *ppv = (LPVOID)iface; + + if (*ppv) + { + IUnknown_AddRef((IUnknown *)(*ppv)); + return S_OK; + } + return E_NOINTERFACE; +} + +static ULONG WINAPI NullWriterEnum_AddRef(IEnumMediaTypes * iface) +{ + ICOM_THIS_MULTI(NullWriterImpl, enummt, iface); + return IBaseFilter_AddRef((IBaseFilter *)This);; +} + +static ULONG WINAPI NullWriterEnum_Release(IEnumMediaTypes * iface) +{ + ICOM_THIS_MULTI(NullWriterImpl, enummt, iface); + return IBaseFilter_Release((IBaseFilter *)This); +} + +static HRESULT WINAPI NullWriterEnum_Next(IEnumMediaTypes * iface, ULONG count, AM_MEDIA_TYPE **mt, ULONG *fetched) +{ + ICOM_THIS_MULTI(NullWriterImpl, enummt, iface); + + if (This->pos) + { + if (fetched) + *fetched = 0; + + return S_FALSE; + } + + if (fetched) + *fetched = 1; + *mt = CoTaskMemAlloc(sizeof(This->amt)); + memcpy(*mt, &This->amt, sizeof(This->amt)); + (*mt)->pbFormat = CoTaskMemAlloc((*mt)->cbFormat); + memcpy((*mt)->pbFormat, This->amt.pbFormat, This->amt.cbFormat); + This->pos = 1; + return (count == 1 ? S_OK : S_FALSE); +} + +static HRESULT WINAPI NullWriterEnum_Skip(IEnumMediaTypes * iface, ULONG count) +{ + ICOM_THIS_MULTI(NullWriterImpl, enummt, iface); + + if (count || This->pos) + return S_FALSE; + else + return S_OK; +} + +static HRESULT WINAPI NullWriterEnum_Reset(IEnumMediaTypes * iface) +{ + ICOM_THIS_MULTI(NullWriterImpl, enummt, iface); + + This->pos = 0; + return S_OK; +} + +static HRESULT WINAPI NullWriterEnum_Clone(IEnumMediaTypes * iface, IEnumMediaTypes **out) +{ + *out = NULL; + return E_NOTIMPL; +} + +static const IEnumMediaTypesVtbl NullWriterEnum_Vtbl = +{ + NullWriterEnum_QueryInterface, + NullWriterEnum_AddRef, + NullWriterEnum_Release, + NullWriterEnum_Next, + NullWriterEnum_Skip, + NullWriterEnum_Reset, + NullWriterEnum_Clone +}; + +static IUnknown *pvmr9 = NULL; +static IDirect3D9 *d3d9 = NULL; + +static LRESULT CALLBACK wndproc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + return DefWindowProcA(hwnd, uMsg, wParam, lParam); +} + +#define SIDE 65 +#define BMBITS 32 +#define BMBYTES (BMBITS / 8 + !!(BMBITS % 8)) + +static IMediaSample *sample; + +static DWORD CALLBACK MemInput_Receive(void *imempin) +{ + return IMemInputPin_Receive((IMemInputPin *)imempin, sample); +} + +static void test_playback(IVMRWindowlessControl9 *windowless) +{ + IBaseFilter *base_null, *base_vmr9 = NULL; + IPin *pin_null, *pin_vmr9 = NULL; + IReferenceClock *clock = NULL; + IEnumPins *enumpins = NULL; + IMemAllocator *allocator = NULL; + IMemInputPin *mempin = NULL; + HANDLE handle; + + VIDEOINFOHEADER2 header2; + AM_MEDIA_TYPE amt; + HRESULT hr; + BYTE *ptr = NULL; + LONGLONG time, time1 = 0, time2 = 10000000; + FILTER_STATE state; + HWND hwnd; + HDC dc = 0; + RECT dest; + ALLOCATOR_PROPERTIES props, actual; + INT y; + + static int wnd_class_registered; + WNDCLASSA winclass; + + sample = NULL; + if (NullWriter_create(&base_null, &pin_null) != S_OK) + { + skip("Could not create null filter, skipping tests\n"); + return; + } + + winclass.style = 0; + winclass.lpfnWndProc = wndproc; + winclass.cbClsExtra = 0; + winclass.cbWndExtra = 0; + winclass.hInstance = NULL; + winclass.hIcon = NULL; + winclass.hCursor = NULL; + winclass.hbrBackground = GetStockObject(BLACK_BRUSH); + winclass.lpszMenuName = NULL; + winclass.lpszClassName = "Wine test window class"; + + if (!wnd_class_registered) + { + if (!RegisterClassA(&winclass)) + { + skip("Could not register window class\n"); + IBaseFilter_Release(base_null); + IPin_Release(pin_null); + return; + } + wnd_class_registered = TRUE; + } + trace("Frames: %u/%u\n", GetSystemMetrics(SM_CXFRAME), GetSystemMetrics(SM_CYCAPTION)); + hwnd = CreateWindowExA(0, "Wine test window class", "Wine ActiveMovie Window", WS_SIZEBOX|WS_CAPTION, + 0, 0, 2 * (SIDE + GetSystemMetrics(SM_CXFRAME)), + 2 * (SIDE + GetSystemMetrics(SM_CYFRAME)) + GetSystemMetrics(SM_CYCAPTION), + NULL, NULL, GetModuleHandle(0), NULL); + ok(!!hwnd, "Could not create window! %08x\n", GetLastError()); + if (!hwnd) + goto out; + + ShowWindow(hwnd, 1); + dest.top = (SIDE / 2); + dest.left = (SIDE / 2); + dest.right = SIDE + dest.left; + dest.bottom = SIDE + dest.top; + + trace("Output rectangle: starting at %dx%d, up to point %dx%d\n", dest.left, dest.top, dest.right, dest.bottom); + if (windowless) + { + IVMRWindowlessControl9_SetVideoPosition(windowless, NULL, &dest); + IVMRWindowlessControl9_SetBorderColor(windowless, D3DCOLOR_XRGB(0x80, 0x80, 0x80)); + } + + hr = CoCreateInstance(&CLSID_SystemClock, NULL, CLSCTX_INPROC_SERVER, + &IID_IReferenceClock, (LPVOID*)&clock); + if (hr != S_OK) + { + skip("Could not create system reference clock (%08x), skipping tests\n", hr); + goto out; + } + + IBaseFilter_SetSyncSource(base_null, clock); + IUnknown_QueryInterface(pvmr9, &IID_IBaseFilter, (void **)&base_vmr9); + hr = IBaseFilter_SetSyncSource(base_vmr9, clock); + ok(hr == S_OK, "Could not set clock to vmr9: %08x\n", hr); + + hr = IBaseFilter_EnumPins(base_vmr9, &enumpins); + ok(hr == S_OK, "Could not get IEnumPins from vmr9 filter: %08x\n", hr); + if (hr != S_OK) + goto out; + + hr = IEnumPins_Next(enumpins, 1, &pin_vmr9, NULL); + ok(hr == S_OK, "Could not get vmr9 pin\n"); + IEnumPins_Release(enumpins); + enumpins = NULL; + if (hr != S_OK) + goto out; + + amt.majortype = MEDIATYPE_Video; + amt.subtype = MEDIASUBTYPE_RGB32; + amt.bFixedSizeSamples = 1; + amt.bTemporalCompression = 0; + amt.lSampleSize = (SIDE * SIDE * BMBYTES); + amt.formattype = FORMAT_VideoInfo2; + amt.pUnk = NULL; + amt.cbFormat = sizeof(header2); + amt.pbFormat = (BYTE *)&header2; + + header2.rcSource.left = header2.rcSource.top = 0; + header2.rcSource.right = header2.rcSource.bottom = SIDE; + header2.rcTarget = header2.rcSource; + header2.dwBitRate = amt.lSampleSize; + header2.dwBitErrorRate = 0; + header2.AvgTimePerFrame = 10000000; + header2.dwInterlaceFlags = 0; + header2.dwCopyProtectFlags = 0; + header2.dwPictAspectRatioX = 1; + header2.dwPictAspectRatioY = 1; + header2.u.dwControlFlags = 0; + header2.dwReserved2 = 0; + + header2.bmiHeader.biSize = sizeof(header2.bmiHeader); + header2.bmiHeader.biWidth = SIDE; + header2.bmiHeader.biHeight = SIDE; + header2.bmiHeader.biPlanes = 1; + header2.bmiHeader.biBitCount = BMBITS; + header2.bmiHeader.biCompression = BI_RGB; + header2.bmiHeader.biSizeImage = SIDE * SIDE * BMBYTES; + header2.bmiHeader.biXPelsPerMeter = header2.bmiHeader.biYPelsPerMeter = 0; + header2.bmiHeader.biClrUsed = 0; + header2.bmiHeader.biClrImportant = 0; + + /* I haven't looked into this yet, earlier tests seemed to indicate + * that a clipping window needs to be set, maybe it's inconsistent? + */ + if (windowless) + { + hr = IPin_Connect(pin_null, pin_vmr9, &amt); + ok(hr == S_OK, "Didn't manage to connect to vmr without clipping window set!\n"); + IVMRWindowlessControl9_SetVideoClippingWindow(windowless, hwnd); + } + + if (hr != S_OK || !windowless) + hr = IPin_Connect(pin_null, pin_vmr9, &amt); + ok(hr == S_OK, "Could not connect to vmr9! %08x\n", hr); + if (hr != S_OK) + goto out; + if (windowless) + IVMRWindowlessControl9_SetVideoPosition(windowless, NULL, &dest); + + hr = IPin_QueryInterface(pin_vmr9, &IID_IMemInputPin, (void **)&mempin); + ok(hr == S_OK && mempin, "VMR9 has no IMemInputPin: %08x (%p)\n", hr, mempin); + if (hr != S_OK || !mempin) + goto out; + + hr = IMemInputPin_GetAllocator(mempin, &allocator); + ok(hr == S_OK, "VMR9 returned %08x on requesting allocator\n", hr); + + memset(&props, 0, sizeof(props)); + if (!allocator) + { + hr = CoCreateInstance(&CLSID_MemoryAllocator, NULL, CLSCTX_INPROC_SERVER, &IID_IMemAllocator, (LPVOID*)&allocator); + ok(hr == S_OK, "Could not create memory allocator!\n"); + if (hr != S_OK) + goto out; + trace("Providing custom allocator as fallback\n"); + } + else + { + IMemAllocator *shadow = NULL; + + hr = IMemInputPin_GetAllocatorRequirements(mempin, &props); + ok(hr == E_NOTIMPL, "Returned %08x\n", hr); + + hr = CoCreateInstance(&CLSID_MemoryAllocator, NULL, CLSCTX_INPROC_SERVER, &IID_IMemAllocator, (LPVOID*)&shadow); + ok(hr == S_OK, "Could not create testing memory allocator!\n"); + + hr = IMemInputPin_NotifyAllocator(mempin, shadow, 0); + ok(hr == E_FAIL, "Setting custom allocator: %08x\n", hr); + + IMemAllocator_Release(shadow); + + hr = IMemInputPin_NotifyAllocator(mempin, allocator, 0); + ok(hr == S_OK, "Setting pins default allocator: %08x\n", hr); + } + + hr = IMemAllocator_GetProperties(allocator, &actual); + ok (hr == S_OK, "Requesting properties returned %08x\n", hr); + if (hr == S_OK) + { + memset(&props, 0, sizeof(props)); + trace("cBuffers: %ld\n", actual.cBuffers); + trace("cbBuffer: %ld\n", actual.cbBuffer); + trace("cbAlign: %ld\n", actual.cbAlign); + trace("cbPrefix: %ld\n", actual.cbPrefix); + ok(!memcmp(&props, &actual, sizeof(props)), "One of the parameters wasn't 0!\n"); + } + + hr = IMemAllocator_SetProperties(allocator, &props, &actual); + ok (hr == VFW_E_BADALIGN, "Requesting properties returned %08x\n", hr); + + props.cBuffers = 2; + props.cbBuffer = SIDE * SIDE * BMBYTES; + props.cbAlign = 1; + + hr = IMemAllocator_SetProperties(allocator, &props, &actual); + ok(hr == S_OK, "Couldn't set!!! OW OW OW OW! %08x\n", hr); + + trace("cBuffers: %ld/%ld\n", props.cBuffers, actual.cBuffers); + trace("cbBuffer: %ld/%ld\n", props.cbBuffer, actual.cbBuffer); + trace("cbAlign: %ld/%ld\n", props.cbAlign, actual.cbAlign); + trace("cbPrefix: %ld/%ld\n", props.cbPrefix, actual.cbPrefix); + + hr = IMemAllocator_Commit(allocator); + ok(hr == S_OK, "Couldn't commit!!! OW OW OW OW! %08x\n", hr); + + hr = IMemAllocator_GetBuffer(allocator, &sample, NULL, NULL, 0); + ok(hr == S_OK, "Couldn't retrieve buffer to fill: %08x\n", hr); + if (hr != S_OK) + goto out; + + assert(IMediaSample_SetActualDataLength(sample, SIDE * SIDE * BMBYTES) == S_OK); + assert(IMediaSample_GetPointer(sample, &ptr) == S_OK); + + /* Paint the background black */ + dc = GetDC(hwnd); + memset(ptr, 0, SIDE * SIDE * BMBYTES); + SetLastError(0); + StretchDIBits(dc, 0, 0, 2 * SIDE, 2 * SIDE, 0, 0, SIDE, SIDE, ptr, (BITMAPINFO *)&header2.bmiHeader, DIB_RGB_COLORS, SRCCOPY); + ok(!GetLastError(), "Last error is: %08x\n", GetLastError()); + ReleaseDC(hwnd, dc); + + memset(ptr, 255, SIDE * SIDE * BMBYTES); + IMediaSample_SetTime(sample, &time1, &time2); + IMediaSample_SetDiscontinuity(sample, 1); + IMediaSample_SetSyncPoint(sample, 1); + + IBaseFilter_Stop(base_vmr9); + hr = IMemInputPin_Receive(mempin, sample); + ok(hr == VFW_E_WRONG_STATE, "Receiving while stopped returned: %08x\n", hr); + IBaseFilter_Pause(base_vmr9); + hr = IBaseFilter_GetState(base_vmr9, 0, &state); + ok(hr == VFW_S_STATE_INTERMEDIATE, "State is not intermediate: %08x\n", hr); + + handle = CreateThread(NULL, 0, MemInput_Receive, mempin, 0, NULL); + /* Weird.. */ + if (!windowless) + ok(WaitForSingleObject(handle, 100) == WAIT_TIMEOUT, "Waiting for Receive call to return didn't time out\n"); + else + ok(WaitForSingleObject(handle, 100) == WAIT_OBJECT_0, "Waiting for Receive call to return timed out\n"); + + hr = IBaseFilter_GetState(base_vmr9, 0, &state); + ok(hr == S_OK, "State check returned: %08x\n", hr); + ok(state == State_Paused, "State is %d!\n", state); + + IReferenceClock_GetTime(clock, &time); + time += 20000; + IBaseFilter_Run(base_vmr9, time); + + ok(WaitForSingleObject(handle, 1200) == WAIT_OBJECT_0, "Receive call STILL didn't return\n"); + CloseHandle(handle); + IMediaSample_Release(sample); + + hr = IBaseFilter_GetState(base_vmr9, 0, &state); + ok(hr == S_OK, "State check returned: %08x\n", hr); + + hr = IMemAllocator_GetBuffer(allocator, &sample, NULL, NULL, 0); + ok(hr == S_OK, "Couldn't retrieve buffer to fill: %08x\n", hr); + if (hr != S_OK) + goto out; + IMediaSample_SetTime(sample, &time1, &time2); + IMediaSample_SetDiscontinuity(sample, 1); + IMediaSample_SetSyncPoint(sample, 1); + assert(IMediaSample_SetActualDataLength(sample, SIDE * SIDE * BMBYTES) == S_OK); + assert(IMediaSample_GetPointer(sample, &ptr) == S_OK); + + for (y = 0; y < SIDE; ++y) + { + DWORD *data = (DWORD *)ptr + SIDE * y; + INT x; + + for (x = 0; x < SIDE; ++x) + { + data[x] = D3DCOLOR_XRGB(255, y * 4, x * 4); + } + } + + IMediaSample_SetDiscontinuity(sample, 1); + hr = IMemInputPin_Receive(mempin, sample); + + hr = IBaseFilter_GetState(base_vmr9, 0, &state); + ok(hr == S_OK, "State check returned: %08x\n", hr); + +out: + if (sample) + IMediaSample_Release(sample); + IBaseFilter_Pause(base_vmr9); + IBaseFilter_Stop(base_vmr9); + hr = IBaseFilter_GetState(base_vmr9, 0, &state); + ok(hr == S_OK, "State check returned: %08x\n", hr); + + if (allocator) + { + IMemAllocator_Decommit(allocator); + IMemAllocator_Release(allocator); + } + if (clock) + IReferenceClock_Release(clock); + if (mempin) + IMemInputPin_Release(mempin); + if (enumpins) + IEnumPins_Release(enumpins); + if (pin_vmr9) + { + IPin_Disconnect(pin_null); + IPin_Disconnect(pin_vmr9); + IPin_Release(pin_vmr9); + } + if (base_vmr9) + IUnknown_Release(base_vmr9); + IUnknown_Release(pin_null); + IUnknown_Release(base_null); + DestroyWindow(hwnd); +} + +/* See what interfaces exist in the different modes */ +static void test_mode_queryinterface(VMR9Mode mode) +{ + HRESULT hr; + ULONG ref; + IUnknown *iface = NULL; + + if (mode == VMR9Mode_Windowed) + { + hr = IUnknown_QueryInterface(pvmr9, &IID_IBasicVideo, (LPVOID*)&iface); + todo_wine ok(hr == S_OK, "IUnknown_QueryInterface returned %x\n", hr); + todo_wine ok(iface != NULL, "Pointer is NULL\n"); + if (iface) + { + ref = IUnknown_Release(iface); + ok(ref == 2, "IUnknown_Release should return %d, got %d\n", 2, ref); + } + iface = NULL; + + hr = IUnknown_QueryInterface(pvmr9, &IID_IBasicVideo2, (LPVOID*)&iface); + todo_wine ok(hr == S_OK, "IUnknown_QueryInterface returned %x\n", hr); + todo_wine ok(iface != NULL, "Pointer is NULL\n"); + if (iface) + { + ref = IUnknown_Release(iface); + ok(ref == 2, "IUnknown_Release should return %d, got %d\n", 2, ref); + } + iface = NULL; + + hr = IUnknown_QueryInterface(pvmr9, &IID_IVideoWindow, (LPVOID*)&iface); + todo_wine ok(hr == S_OK, "IUnknown_QueryInterface returned %x\n", hr); + todo_wine ok(iface != NULL, "Pointer is NULL\n"); + if (iface) + { + ref = IUnknown_Release(iface); + ok(ref == 2, "IUnknown_Release should return %d, got %d\n", 2, ref); + } + iface = NULL; + test_playback(NULL); + } + else + { + hr = IUnknown_QueryInterface(pvmr9, &IID_IBasicVideo, (LPVOID*)&iface); + ok(hr == E_NOINTERFACE, "IUnknown_QueryInterface returned %x\n", hr); + ok(iface == NULL, "Pointer is NULL\n"); + if (iface) + { + IUnknown_Release(iface); + } + iface = NULL; + + hr = IUnknown_QueryInterface(pvmr9, &IID_IBasicVideo2, (LPVOID*)&iface); + ok(hr == E_NOINTERFACE, "IUnknown_QueryInterface returned %x\n", hr); + ok(iface == NULL, "Pointer is NULL\n"); + if (iface) + { + IUnknown_Release(iface); + } + iface = NULL; + + hr = IUnknown_QueryInterface(pvmr9, &IID_IVideoWindow, (LPVOID*)&iface); + ok(hr == E_NOINTERFACE, "IUnknown_QueryInterface returned %x\n", hr); + ok(iface == NULL, "Pointer is NULL\n"); + if (iface) + { + IUnknown_Release(iface); + } + iface = NULL; + } + + if (mode == VMR9Mode_Windowless) + { + hr = IUnknown_QueryInterface(pvmr9, &IID_IVMRWindowlessControl9, (LPVOID*)&iface); + ok(hr == S_OK, "IUnknown_QueryInterface returned %x\n", hr); + ok(iface != NULL, "Pointer is NULL\n"); + if (iface) + { + test_playback((IVMRWindowlessControl9 *)iface); + ref = IUnknown_Release(iface); + ok(ref == 2 || ref == 3, "IUnknown_Release should return %d (or 3), got %d\n", 2, ref); + } + iface = NULL; + } + else + { + hr = IUnknown_QueryInterface(pvmr9, &IID_IVMRWindowlessControl9, (LPVOID*)&iface); + ok(hr == E_NOINTERFACE, "IUnknown_QueryInterface returned %x\n", hr); + ok(iface == NULL, "Pointer is NULL\n"); + if (iface) + { + IUnknown_Release(iface); + } + iface = NULL; + } + + if (mode == VMR9Mode_Renderless) + { + hr = IUnknown_QueryInterface(pvmr9, &IID_IVMRSurfaceAllocatorNotify9, (LPVOID*)&iface); + ok(hr == S_OK, "IUnknown_QueryInterface returned %x\n", hr); + ok(iface != NULL, "Pointer is NULL\n"); + if (iface) + { + ref = IUnknown_Release(iface); + ok(ref == 0, "IUnknown_Release should return %d, got %d\n", 0, ref); + } + iface = NULL; + } + else + { + hr = IUnknown_QueryInterface(pvmr9, &IID_IVMRSurfaceAllocatorNotify9, (LPVOID*)&iface); + ok(hr == E_NOINTERFACE, "IUnknown_QueryInterface returned %x\n", hr); + ok(iface == NULL, "Pointer is NULL\n"); + if (iface) + { + IUnknown_Release(iface); + } + iface = NULL; + } +} + +static int test_obtain_interface(IVMRFilterConfig9 **cfg) +{ + HRESULT hr; + hr = CoCreateInstance(&CLSID_VideoMixingRenderer9, NULL, CLSCTX_INPROC_SERVER, + &IID_IUnknown, (LPVOID*)&pvmr9); + + ok(hr == S_OK, "Could not create vmr9 interface: %08x\n", hr); + if (hr != S_OK) + return 0; + + hr = IUnknown_QueryInterface(pvmr9, &IID_IVMRFilterConfig9, (void **)cfg); + ok(hr == S_OK, "Could not create IVMRFilterConfig9 interface: %08x\n", hr); + if (hr != S_OK) + { + IUnknown_Release(pvmr9); + return 0; + } + return 1; +} + +/* This needs to be a separate test since it creates the interface over and over again */ +static void test_vmrmode(void) +{ + IVMRFilterConfig9 *cfg = NULL; + VMR9Mode mode; + HRESULT hr; + + if (!test_obtain_interface(&cfg)) return; + hr = IVMRFilterConfig9_GetRenderingMode(cfg, NULL); + ok(hr == E_POINTER, "Expected E_POINTER on null argument, got: %08x\n", hr); + + hr = IVMRFilterConfig9_SetRenderingMode(cfg, VMR9Mode_Mask); + ok(hr == E_INVALIDARG, "Expected E_INVALIDARG on invalid argument, got: %08x\n", hr); + + hr = IVMRFilterConfig9_GetRenderingMode(cfg, &mode); + ok(hr == S_OK, "Could not obtain mode: %08x\n", hr); + ok(mode == VMR9Mode_Windowed, "Mode is %x instead of %x\n", mode, VMR9Mode_Windowed); + if (hr != S_OK) + goto die; + + if (mode == VMR9Mode_Windowed) + { + /* This should be a noop, but apparantly isn't */ + hr = IVMRFilterConfig9_SetRenderingMode(cfg, mode); + ok(hr == S_OK, "Expected S_OK on setting same mode, got: %08x\n", hr); + + hr = IVMRFilterConfig9_SetRenderingMode(cfg, mode); + ok(hr == VFW_E_WRONG_STATE, "Expected VFW_E_WRONG_STATE on setting same mode twice, got: %08x\n", hr); + + test_mode_queryinterface(VMR9Mode_Windowed); + } + hr = IUnknown_Release(cfg); + ok(hr == 1, "ref = %d, expected %d\n", hr, 1); + hr = IUnknown_Release(pvmr9); + ok(!hr, "ref = %d, expected %d\n", hr, 0); + + if (!test_obtain_interface(&cfg)) return; + hr = IVMRFilterConfig9_SetRenderingMode(cfg, VMR9Mode_Windowless); + ok(hr == S_OK, "Could not set mode: %08x\n", hr); + + hr = IVMRFilterConfig9_GetRenderingMode(cfg, &mode); + ok(hr == S_OK, "Could not obtain mode: %08x\n", hr); + ok(mode == VMR9Mode_Windowless, "Mode is %x instead of %x\n", mode, VMR9Mode_Windowless); + if (mode == VMR9Mode_Windowless) + test_mode_queryinterface(VMR9Mode_Windowless); + + hr = IUnknown_Release(cfg); + ok(hr == 1 || hr == 2, "ref = %d, expected %d (or %d)\n", hr, 1, 2); + hr = IUnknown_Release(pvmr9); + ok(!hr || hr == 1, "ref = %d, expected %d (or %d)\n", hr, 0, 1); + + if (!test_obtain_interface(&cfg)) return; + hr = IVMRFilterConfig9_SetRenderingMode(cfg, VMR9Mode_Renderless); + ok(hr == S_OK, "Could not set mode: %08x\n", hr); + + hr = IVMRFilterConfig9_GetRenderingMode(cfg, &mode); + ok(hr == S_OK, "Could not obtain mode: %08x\n", hr); + ok(mode == VMR9Mode_Renderless, "Mode is %x instead of %x\n", mode, VMR9Mode_Renderless); + + if (mode == VMR9Mode_Renderless) + test_mode_queryinterface(VMR9Mode_Renderless); + +die: + hr = IUnknown_Release(cfg); + ok(hr == 1, "ref = %d, expected %d\n", hr, 1); + hr = IUnknown_Release(pvmr9); + ok(!hr, "ref = %d, expected %d\n", hr, 0); +} + +static void test_query_interface(void) +{ + HRESULT hr; + ULONG ref; + IUnknown *iface = NULL; + + hr = IUnknown_QueryInterface(pvmr9, &IID_IBaseFilter, (LPVOID*)&iface); + ok(hr == S_OK, "IUnknown_QueryInterface returned %x\n", hr); + ok(iface != NULL, "Pointer is NULL\n"); + if (iface) + { + ref = IUnknown_Release(iface); + ok(ref == 1, "IUnknown_Release should return %d, got %d\n", 1, ref); + } + iface = NULL; + + hr = IUnknown_QueryInterface(pvmr9, &IID_IMediaSeeking, (LPVOID*)&iface); + ok(hr == S_OK, "IUnknown_QueryInterface returned %x\n", hr); + ok(iface != NULL, "Pointer is NULL\n"); + if (iface) + { + ref = IUnknown_Release(iface); + ok(ref == 1, "IUnknown_Release should return %d, got %d\n", 1, ref); + } + iface = NULL; + + hr = IUnknown_QueryInterface(pvmr9, &IID_IVMRFilterConfig9, (LPVOID*)&iface); + ok(hr == S_OK, "IUnknown_QueryInterface returned %x\n", hr); + ok(iface != NULL, "Pointer is NULL\n"); + if (iface) + { + ref = IUnknown_Release(iface); + ok(ref == 1, "IUnknown_Release should return %d, got %d\n", 1, ref); + } + iface = NULL; + +todo_wine { + hr = IUnknown_QueryInterface(pvmr9, &IID_IAMFilterMiscFlags, (LPVOID*)&iface); + ok(hr == S_OK, "IUnknown_QueryInterface returned %x\n", hr); + ok(iface != NULL, "Pointer is NULL\n"); + if (iface) + { + ref = IUnknown_Release(iface); + ok(ref == 1, "IUnknown_Release should return %d, got %d\n", 1, ref); + } + iface = NULL; + + hr = IUnknown_QueryInterface(pvmr9, &IID_IMediaPosition, (LPVOID*)&iface); + ok(hr == S_OK, "IUnknown_QueryInterface returned %x\n", hr); + ok(iface != NULL, "Pointer is NULL\n"); + if (iface) + { + ref = IUnknown_Release(iface); + ok(ref == 1, "IUnknown_Release should return %d, got %d\n", 1, ref); + } + iface = NULL; + + hr = IUnknown_QueryInterface(pvmr9, &IID_IQualityControl, (LPVOID*)&iface); + ok(hr == S_OK, "IUnknown_QueryInterface returned %x\n", hr); + ok(iface != NULL, "Pointer is NULL\n"); + if (iface) + { + ref = IUnknown_Release(iface); + ok(ref == 1, "IUnknown_Release should return %d, got %d\n", 1, ref); + } + iface = NULL; + + hr = IUnknown_QueryInterface(pvmr9, &IID_IQualProp, (LPVOID*)&iface); + ok(hr == S_OK, "IUnknown_QueryInterface returned %x\n", hr); + ok(iface != NULL, "Pointer is NULL\n"); + if (iface) + { + ref = IUnknown_Release(iface); + ok(ref == 1, "IUnknown_Release should return %d, got %d\n", 1, ref); + } + iface = NULL; + + hr = IUnknown_QueryInterface(pvmr9, &IID_IVMRAspectRatioControl9, (LPVOID*)&iface); + ok(hr == S_OK, "IUnknown_QueryInterface returned %x\n", hr); + ok(iface != NULL, "Pointer is NULL\n"); + if (iface) + { + ref = IUnknown_Release(iface); + ok(ref == 1, "IUnknown_Release should return %d, got %d\n", 1, ref); + } + iface = NULL; + + hr = IUnknown_QueryInterface(pvmr9, &IID_IVMRDeinterlaceControl9, (LPVOID*)&iface); + ok(hr == S_OK, "IUnknown_QueryInterface returned %x\n", hr); + ok(iface != NULL, "Pointer is NULL\n"); + if (iface) + { + ref = IUnknown_Release(iface); + ok(ref == 1, "IUnknown_Release should return %d, got %d\n", 1, ref); + } + iface = NULL; + + hr = IUnknown_QueryInterface(pvmr9, &IID_IVMRMixerBitmap9, (LPVOID*)&iface); + ok(hr == S_OK, "IUnknown_QueryInterface returned %x\n", hr); + ok(iface != NULL, "Pointer is NULL\n"); + if (iface) + { + ref = IUnknown_Release(iface); + ok(ref == 1, "IUnknown_Release should return %d, got %d\n", 1, ref); + } + iface = NULL; + + hr = IUnknown_QueryInterface(pvmr9, &IID_IVMRMonitorConfig9, (LPVOID*)&iface); + ok(hr == S_OK, "IUnknown_QueryInterface returned %x\n", hr); + ok(iface != NULL, "Pointer is NULL\n"); + if (iface) + { + ref = IUnknown_Release(iface); + ok(ref == 1, "IUnknown_Release should return %d, got %d\n", 1, ref); + } + iface = NULL; +} /* todo_wine */ + +/* Apparantly this one returns E_NOINTERFACE, despite msdn saying it exists + * It looks like it only exists when IVMRFilterConfig9::SetNumberOfStreams is called + */ + hr = IUnknown_QueryInterface(pvmr9, &IID_IVMRMixerControl9, (LPVOID*)&iface); + ok(hr == E_NOINTERFACE, "IUnknown_QueryInterface returned %x\n", hr); + if (iface) + { + ref = IUnknown_Release(iface); + ok(ref == 1, "IUnknown_Release should return %d, got %d\n", 1, ref); + } + iface = NULL; +} + +static void test_pin(IPin *pin) +{ + HRESULT hr; + IMemInputPin *mpin = NULL; + + IPin_QueryInterface(pin, &IID_IMemInputPin, (void **)&mpin); + + ok(mpin != NULL, "No IMemInputPin found!\n"); + if (mpin) + { + ok(IMemInputPin_ReceiveCanBlock(mpin) == S_OK, "Receive can't block for pin!\n"); + hr = IMemInputPin_NotifyAllocator(mpin, NULL, 0); + ok(hr == E_FAIL || hr == E_POINTER, "NotifyAllocator with null returns %08x\n", hr); + IMemInputPin_Release(mpin); + } + /* TODO */ +} + +static void test_basefilter(void) +{ + IEnumPins *pin_enum = NULL; + IBaseFilter *base = NULL; + IPin *pins[2]; + ULONG ref; + ULONG ref_base; + ULONG ref_unk; + HRESULT hr; + + IUnknown_QueryInterface(pvmr9, &IID_IBaseFilter, (void *)&base); + if (base == NULL) + { + /* test_query_interface handles this case */ + skip("No IBaseFilter\n"); + return; + } + + ref_unk = IUnknown_AddRef(pvmr9); + ref_base = IUnknown_AddRef(base); + ok(ref_unk + 2 == IUnknown_AddRef(pvmr9), "IUnknown and BaseFilter references should be interconnected!\n"); + IUnknown_Release(pvmr9); + IUnknown_Release(base); + IUnknown_Release(pvmr9); + + hr = IBaseFilter_EnumPins(base, NULL); + ok(hr == E_POINTER, "hr = %08x and not E_POINTER\n", hr); + + hr= IBaseFilter_EnumPins(base, &pin_enum); + ok(hr == S_OK, "hr = %08x and not S_OK\n", hr); + + hr = IEnumPins_Next(pin_enum, 1, NULL, NULL); + ok(hr == E_POINTER, "hr = %08x and not E_POINTER\n", hr); + + hr = IEnumPins_Next(pin_enum, 2, pins, NULL); + ok(hr == E_INVALIDARG, "hr = %08x and not E_INVALIDARG\n", hr); + + pins[0] = (void *)0xdead; + pins[1] = (void *)0xdeed; + + hr = IEnumPins_Next(pin_enum, 2, pins, &ref); + ok(hr == S_FALSE, "hr = %08x instead of S_FALSE\n", hr); + ok(pins[0] != (void *)0xdead && pins[0] != NULL, "pins[0] = %p\n", pins[0]); + if (pins[0] != (void *)0xdead && pins[0] != NULL) + { + test_pin(pins[0]); + IPin_Release(pins[0]); + } + + ok(pins[1] == (void *)0xdeed, "pins[1] = %p\n", pins[1]); + + ref = IEnumPins_Release(pin_enum); + ok(ref == 0, "ref is %u and not 0!\n", ref); + + IBaseFilter_Release(base); +} + +static void test_rendering() +{ + IGraphBuilder *pgraph = NULL; + ICaptureGraphBuilder2 *pbuild = NULL; + IBaseFilter *source = NULL; + IBaseFilter *renderer = NULL; + IMediaControl *media = NULL; + HRESULT hr; + + static const WCHAR sourceW[] = {'s','o','u','r','c','e',' ','f','i','l','t','e','r',0}; + static const WCHAR vmr9W[] = {'v','m','r','9',0}; + static const WCHAR file[] = {'t','e','s','t','.','a','v','i',0}; + + IUnknown_QueryInterface(pvmr9, &IID_IBaseFilter, (void **)&renderer); + ok(renderer != NULL, "VMR-9 has no IBaseFilter!\n"); + if (!renderer) + return; + + hr = CoCreateInstance(&CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, + &IID_IGraphBuilder, (LPVOID*)&pgraph); + if (hr != S_OK || pgraph == NULL) + { + skip("Could not create IFiltergraph2\n"); + goto cleanup; + } + IUnknown_QueryInterface(pgraph, &IID_IMediaControl, (void **)&media); + if (!media) + { + skip("Could not get media control from IFiltergraph2\n"); + goto cleanup; + } + + hr = CoCreateInstance(&CLSID_CaptureGraphBuilder2, NULL, CLSCTX_INPROC_SERVER, + &IID_ICaptureGraphBuilder2, (LPVOID*)&pbuild); + if (hr != S_OK || pbuild == NULL || ICaptureGraphBuilder2_SetFiltergraph(pbuild, pgraph) != S_OK) + { + skip("Could not create IFiltergraph2\n"); + goto cleanup; + } + + if (FAILED(IGraphBuilder_AddFilter(pgraph, renderer, vmr9W))) + { + skip("Could not add vmr to IFiltergraph2\n"); + goto cleanup; + } + + if (FAILED(IGraphBuilder_AddSourceFilter(pgraph, file, sourceW, &source)) || !source) + { + skip("Could not add source filter to IFiltergraph2 (Does test.avi exist?)\n"); + goto cleanup; + } + + if (FAILED(ICaptureGraphBuilder2_RenderStream(pbuild, NULL, NULL, (IUnknown *)source, NULL, renderer))) + { + skip("Could not render video\n"); + goto cleanup; + } + + IMediaControl_Run(media); + IMediaControl_Stop(media); + +cleanup: + if (media) + IUnknown_Release(media); + if (renderer) + IUnknown_Release(renderer); + if (source) + IUnknown_Release(source); + if (pgraph) + IUnknown_Release(pgraph); + if (pbuild) + IUnknown_Release(pbuild); +} + +static void init_d3d9(HMODULE d3d9_handle) +{ + IDirect3D9 * (__stdcall * d3d9_create)(UINT SDKVersion); + + d3d9_create = (void *)GetProcAddress(d3d9_handle, "Direct3DCreate9"); + if (!d3d9_create) return; + + d3d9 = d3d9_create(D3D_SDK_VERSION); +} + +START_TEST(vmr9) +{ + HRESULT hr; + HANDLE d3d9_handle; + + CoInitialize(NULL); + d3d9_handle = LoadLibraryA("d3d9"); + + hr = CoCreateInstance(&CLSID_VideoMixingRenderer9, NULL, CLSCTX_INPROC_SERVER, + &IID_IUnknown, (LPVOID*)&pvmr9); + + if (hr == S_OK) + ok(pvmr9 != NULL, "CoCreateInstance succeeded, but no VMR-9 interface was retrieved\n"); + ok(hr == S_OK || hr == VFW_E_DDRAW_CAPS_NOT_SUITABLE || hr == REGDB_E_CLASSNOTREG, "Unexpected hr %08x returned\n", hr); + + if (d3d9_handle) + init_d3d9(d3d9_handle); + + if (hr != S_OK || !pvmr9 || !d3d9) + { + if (d3d9) + skip("Could not create vmr9 interface, skipping tests\n"); + else + skip("Could not load d3d9, skipping tests\n"); + return; + } + + test_query_interface(); + test_basefilter(); + hr = IUnknown_Release(pvmr9); + ok(hr == 0, "IUnknown_Release failed with %d\n", hr); + + hr = CoCreateInstance(&CLSID_VideoMixingRenderer9, NULL, CLSCTX_INPROC_SERVER, + &IID_IUnknown, (LPVOID*)&pvmr9); + ok(hr == S_OK, "Could not re-create interface!\n"); + if (hr != S_OK) return; + test_rendering(); + + IUnknown_Release(pvmr9); + IUnknown_Release(d3d9); + + /* This will create its own interface */ + test_vmrmode(); + +} -- 1.5.4.1