From 0cfbcaf0666749a1e48a8acd7009c6987e310549 Mon Sep 17 00:00:00 2001 From: Maarten Lankhorst Date: Tue, 29 Jul 2008 12:44:11 -0700 Subject: [PATCH] quartz: Add initial vmr-9 stub --- dlls/quartz/Makefile.in | 3 +- dlls/quartz/main.c | 1 + dlls/quartz/quartz_private.h | 1 + dlls/quartz/regsvr.c | 19 + dlls/quartz/tests/vmr9.c | 6 +- dlls/quartz/vmr9.c | 910 ++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 936 insertions(+), 4 deletions(-) create mode 100644 dlls/quartz/vmr9.c diff --git a/dlls/quartz/Makefile.in b/dlls/quartz/Makefile.in index c5114b9..36eb785 100644 --- a/dlls/quartz/Makefile.in +++ b/dlls/quartz/Makefile.in @@ -4,7 +4,7 @@ SRCDIR = @srcdir@ VPATH = @srcdir@ MODULE = quartz.dll IMPORTLIB = quartz -IMPORTS = strmiids uuid dsound msacm32 msvfw32 ole32 oleaut32 shlwapi user32 gdi32 advapi32 kernel32 +IMPORTS = strmiids dxguid uuid dsound msacm32 msvfw32 ole32 oleaut32 shlwapi user32 gdi32 advapi32 kernel32 C_SRCS = \ acmwrapper.c \ @@ -30,6 +30,7 @@ C_SRCS = \ systemclock.c \ transform.c \ videorenderer.c \ + vmr9.c \ waveparser.c RC_SRCS = version.rc diff --git a/dlls/quartz/main.c b/dlls/quartz/main.c index daefdc5..4d16973 100644 --- a/dlls/quartz/main.c +++ b/dlls/quartz/main.c @@ -71,6 +71,7 @@ static const struct object_creation_info object_creation[] = { &CLSID_MPEG1Splitter, MPEGSplitter_create }, { &CLSID_VideoRenderer, VideoRenderer_create }, { &CLSID_NullRenderer, NullRenderer_create }, + { &CLSID_VideoMixingRenderer9, VMR9Impl_create }, { &CLSID_VideoRendererDefault, VideoRendererDefault_create }, { &CLSID_DSoundRender, DSoundRender_create }, { &CLSID_AudioRender, DSoundRender_create }, diff --git a/dlls/quartz/quartz_private.h b/dlls/quartz/quartz_private.h index e025078..8794511 100644 --- a/dlls/quartz/quartz_private.h +++ b/dlls/quartz/quartz_private.h @@ -59,6 +59,7 @@ HRESULT QUARTZ_CreateSystemClock(IUnknown * pUnkOuter, LPVOID * ppv); HRESULT ACMWrapper_create(IUnknown * pUnkOuter, LPVOID * ppv); HRESULT WAVEParser_create(IUnknown * pUnkOuter, LPVOID * ppv); HRESULT SeekingPassThru_create(IUnknown *pUnkOuter, LPVOID *ppObj); +HRESULT VMR9Impl_create(IUnknown *pUnkOuter, LPVOID *ppv); HRESULT EnumMonikerImpl_Create(IMoniker ** ppMoniker, ULONG nMonikerCount, IEnumMoniker ** ppEnum); diff --git a/dlls/quartz/regsvr.c b/dlls/quartz/regsvr.c index 79000bf..6ff4d42 100644 --- a/dlls/quartz/regsvr.c +++ b/dlls/quartz/regsvr.c @@ -874,6 +874,12 @@ static struct regsvr_coclass const coclass_list[] = { "quartz.dll", "Both" }, + { &CLSID_VideoMixingRenderer9, + "Video Mixing Renderer v9", + NULL, + "quartz.dll", + "Both" + }, { &CLSID_VideoRendererDefault, "Default Video Renderer", NULL, @@ -1118,6 +1124,18 @@ static struct regsvr_filter const filter_list[] = { { 0xFFFFFFFF }, } }, + { &CLSID_VideoMixingRenderer9, + &CLSID_LegacyAmFilterCategory, + {'V','i','d','e','o',' ','M','i','x','i','n','g',' ','R','e','n','d','e','r','e','r',' ','9',0}, + 0x200000, + { { REG_PINFLAG_B_RENDERER, + { { &MEDIATYPE_Video, &GUID_NULL }, + { NULL } + }, + }, + { 0xFFFFFFFF }, + } + }, { &CLSID_DSoundRender, &CLSID_LegacyAmFilterCategory, {'A','u','d','i','o',' ','R','e','n','d','e','r','e','r',0}, @@ -1253,3 +1271,4 @@ HRESULT WINAPI DllUnregisterServer(void) hr = unregister_mediatypes_extension(mediatype_extension_list); return hr; } + diff --git a/dlls/quartz/tests/vmr9.c b/dlls/quartz/tests/vmr9.c index 4d8d427..2d3b6e3 100644 --- a/dlls/quartz/tests/vmr9.c +++ b/dlls/quartz/tests/vmr9.c @@ -512,7 +512,7 @@ static int test_obtain_interface(IVMRFilterConfig9 **cfg) return 0; hr = IUnknown_QueryInterface(pvmr9, &IID_IVMRFilterConfig9, (void **)cfg); - ok(hr == S_OK, "Could not create IVMRFilterConfig9 interface: %08x\n", hr); + todo_wine ok(hr == S_OK, "Could not create IVMRFilterConfig9 interface: %08x\n", hr); if (hr != S_OK) { IUnknown_Release(pvmr9); @@ -617,8 +617,8 @@ static void test_query_interface(void) 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"); + 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); diff --git a/dlls/quartz/vmr9.c b/dlls/quartz/vmr9.c new file mode 100644 index 0000000..0f2228a --- /dev/null +++ b/dlls/quartz/vmr9.c @@ -0,0 +1,910 @@ +/* + * Video Mixing Renderer for dx9 + * + * Copyright 2004 Christian Costa + * Copyright 2008 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 + */ + +#include "config.h" +#include "assert.h" + +#define NONAMELESSSTRUCT +#define NONAMELESSUNION +#include "quartz_private.h" + +#include "uuids.h" +#include "vfwmsgs.h" +#include "amvideo.h" +#include "windef.h" +#include "winbase.h" +#include "dshow.h" +#include "evcode.h" +#include "strmif.h" +#include "ddraw.h" +#include "dvdmedia.h" +#include "d3d9.h" +#include "vmr9.h" +#include "pin.h" +#include "control_private.h" + +#include "wine/unicode.h" +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(quartz); + + +static const WCHAR wcsInputPinName[] = {'i','n','p','u','t',' ','p','i','n',0}; + +static const IBaseFilterVtbl VMR9_Vtbl; +static const IUnknownVtbl IInner_VTable; +static const IPinVtbl VMR9_InputPin_Vtbl; + +typedef struct VMR9Impl +{ + const IBaseFilterVtbl * lpVtbl; + const IUnknownVtbl *IInner_vtbl; + + /* VMR9 basic filter */ + LONG ref; + LONG ref_surface; + + CRITICAL_SECTION csFilter; + FILTER_STATE state; + REFERENCE_TIME rtStreamStart, rtLastStop; + IReferenceClock * pClock; + FILTER_INFO filterInfo; + VMR9Mode mode; + BITMAPINFOHEADER bmiheader; + InputPin *pInputPin; + IUnknown * pUnkOuter; + BOOL bUnkOuterValid; + BOOL bAggregatable; + MediaSeekingImpl mediaSeeking; + IMediaSample *sample_held; + + HANDLE state_handle, blocked, ack; +} VMR9Impl; + +static const IMemInputPinVtbl MemInputPin_Vtbl = +{ + MemInputPin_QueryInterface, + MemInputPin_AddRef, + MemInputPin_Release, + MemInputPin_GetAllocator, + MemInputPin_NotifyAllocator, + MemInputPin_GetAllocatorRequirements, + MemInputPin_Receive, + MemInputPin_ReceiveMultiple, + MemInputPin_ReceiveCanBlock +}; + +static void VMR9Impl_destroy(VMR9Impl *This); + +static HRESULT VMR9_Sample(LPVOID iface, IMediaSample * pSample) +{ + VMR9Impl *This = (VMR9Impl *)iface; + LPBYTE pbSrcStream = NULL; + long cbSrcStream = 0; + REFERENCE_TIME tStart, tStop; + VMR9PresentationInfo info; + HRESULT hr; + + EnterCriticalSection(&This->csFilter); + if (This->state == State_Stopped) + { + LeaveCriticalSection(&This->csFilter); + return VFW_E_WRONG_STATE; + } + + if (This->pInputPin->flushing || This->pInputPin->end_of_stream) + { + LeaveCriticalSection(&This->csFilter); + return S_FALSE; + } + + TRACE("%p %p\n", iface, pSample); + + hr = IMediaSample_GetTime(pSample, &tStart, &tStop); + if (FAILED(hr)) + info.dwFlags = VMR9Sample_SrcDstRectsValid; + else + info.dwFlags = VMR9Sample_SrcDstRectsValid | VMR9Sample_TimeValid; + + if (IMediaSample_IsDiscontinuity(pSample) == S_OK) + info.dwFlags |= VMR9Sample_Discontinuity; + + if (IMediaSample_IsPreroll(pSample) == S_OK) + info.dwFlags |= VMR9Sample_Preroll; + + if (IMediaSample_IsSyncPoint(pSample) == S_OK) + info.dwFlags |= VMR9Sample_SyncPoint; + + if (This->rtLastStop != tStart) + { + if (IMediaSample_IsDiscontinuity(pSample) == S_FALSE) + ERR("Unexpected discontinuity: Last: %u.%03u, tStart: %u.%03u\n", + (DWORD)(This->rtLastStop / 10000000), + (DWORD)((This->rtLastStop / 10000)%1000), + (DWORD)(tStart / 10000000), (DWORD)((tStart / 10000)%1000)); + + if (This->pClock) + { + REFERENCE_TIME now; + + IReferenceClock_GetTime(This->pClock, &now); + This->rtStreamStart = now; + } + } + This->rtLastStop = tStop; + + hr = IMediaSample_GetPointer(pSample, &pbSrcStream); + if (FAILED(hr)) + { + ERR("Cannot get pointer to sample data (%x)\n", hr); + LeaveCriticalSection(&This->csFilter); + return hr; + } + + cbSrcStream = IMediaSample_GetActualDataLength(pSample); + +#if 0 /* For debugging purpose */ + { + int i; + for(i = 0; i < cbSrcStream; i++) + { + if ((i!=0) && !(i%16)) + TRACE("\n"); + TRACE("%02x ", pbSrcStream[i]); + } + TRACE("\n"); + } +#endif + + if (This->pClock && This->state == State_Running) + { + REFERENCE_TIME time, trefstop; + LONG delta; + + /* Perhaps I use the reference clock AdviseTime function here + * I'm not going to! When I tried, it seemed to generate lag and + * it caused instability. + */ + IReferenceClock_GetTime(This->pClock, &time); + + trefstop = This->rtStreamStart; + delta = (LONG)((trefstop-time)/10000); + This->rtStreamStart += (double)(tStop - tStart) / This->pInputPin->dRate; + + if (delta > 0) + { + if (delta > 100) + WARN("Sleeping for %u ms\n", delta); + Sleep(delta); + } + else if (time > trefstop) + { + WARN("Dropping sample: Time: %lld ms trefstop: %lld ms!\n", time/10000, trefstop/10000); + LeaveCriticalSection(&This->csFilter); + return S_OK; + } + } + + info.rtStart = tStart; + info.rtEnd = tStop; + info.szAspectRatio.cx = This->bmiheader.biWidth; + info.szAspectRatio.cy = This->bmiheader.biHeight; + + SetEvent(This->state_handle); + if (This->state == State_Paused) + { + This->sample_held = pSample; + LeaveCriticalSection(&This->csFilter); + WaitForSingleObject(This->blocked, INFINITE); + This->sample_held = NULL; + } + else + LeaveCriticalSection(&This->csFilter); + + return hr; +} + +static HRESULT VMR9_QueryAccept(LPVOID iface, const AM_MEDIA_TYPE * pmt) +{ + VMR9Impl *This = iface; + + if (!IsEqualIID(&pmt->majortype, &MEDIATYPE_Video) || !pmt->pbFormat) + return S_FALSE; + + /* Ignore subtype, test for bicompression instead */ + if (IsEqualIID(&pmt->formattype, &FORMAT_VideoInfo)) + { + VIDEOINFOHEADER *format = (VIDEOINFOHEADER *)pmt->pbFormat; + + This->bmiheader = format->bmiHeader; + TRACE("Resolution: %dx%d\n", format->bmiHeader.biWidth, format->bmiHeader.biHeight); + } + else if (IsEqualIID(&pmt->formattype, &FORMAT_VideoInfo2)) + { + VIDEOINFOHEADER2 *format = (VIDEOINFOHEADER2 *)pmt->pbFormat; + + This->bmiheader = format->bmiHeader; + + TRACE("Resolution: %dx%d\n", format->bmiHeader.biWidth, format->bmiHeader.biHeight); + } + else + { + ERR("Format type %s not supported\n", debugstr_guid(&pmt->formattype)); + return S_FALSE; + } + if (This->bmiheader.biCompression) + return S_FALSE; + return S_OK; +} + +static HRESULT WINAPI VMR9Impl_Seeking_QueryInterface(IMediaSeeking * iface, REFIID riid, LPVOID * ppv) +{ + ICOM_THIS_MULTI(VMR9Impl, mediaSeeking, iface); + + return IUnknown_QueryInterface((IUnknown *)This, riid, ppv); +} + +static ULONG WINAPI VMR9Impl_Seeking_AddRef(IMediaSeeking * iface) +{ + ICOM_THIS_MULTI(VMR9Impl, mediaSeeking, iface); + return IUnknown_AddRef((IBaseFilter *)This); +} + +static ULONG WINAPI VMR9Impl_Seeking_Release(IMediaSeeking * iface) +{ + ICOM_THIS_MULTI(VMR9Impl, mediaSeeking, iface); + return IUnknown_Release((IBaseFilter *)This); +} + +static const IMediaSeekingVtbl VMR9_MediaSeeking_Vtbl = +{ + VMR9Impl_Seeking_QueryInterface, + VMR9Impl_Seeking_AddRef, + VMR9Impl_Seeking_Release, + MediaSeekingImpl_GetCapabilities, + MediaSeekingImpl_CheckCapabilities, + MediaSeekingImpl_IsFormatSupported, + MediaSeekingImpl_QueryPreferredFormat, + MediaSeekingImpl_GetTimeFormat, + MediaSeekingImpl_IsUsingTimeFormat, + MediaSeekingImpl_SetTimeFormat, + MediaSeekingImpl_GetDuration, + MediaSeekingImpl_GetStopPosition, + MediaSeekingImpl_GetCurrentPosition, + MediaSeekingImpl_ConvertTimeFormat, + MediaSeekingImpl_SetPositions, + MediaSeekingImpl_GetPositions, + MediaSeekingImpl_GetAvailable, + MediaSeekingImpl_SetRate, + MediaSeekingImpl_GetRate, + MediaSeekingImpl_GetPreroll +}; + +static HRESULT VMR9Impl_Change(IBaseFilter *iface) +{ + FIXME("(%p)\n", iface); + return S_OK; +} + +HRESULT VMR9Impl_create(IUnknown * pUnkOuter, LPVOID * ppv) +{ + HRESULT hr; + PIN_INFO piInput; + VMR9Impl * pVMR9; + + ERR("(%p, %p)\n", pUnkOuter, ppv); + + *ppv = NULL; + + pVMR9 = CoTaskMemAlloc(sizeof(VMR9Impl)); + + pVMR9->pUnkOuter = pUnkOuter; + pVMR9->bUnkOuterValid = FALSE; + pVMR9->bAggregatable = FALSE; + pVMR9->IInner_vtbl = &IInner_VTable; + + pVMR9->lpVtbl = &VMR9_Vtbl; + + pVMR9->ref = 1; + + InitializeCriticalSection(&pVMR9->csFilter); + pVMR9->csFilter.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": VMR9Impl.csFilter"); + pVMR9->state = State_Stopped; + pVMR9->pClock = NULL; + ZeroMemory(&pVMR9->filterInfo, sizeof(pVMR9->filterInfo)); + pVMR9->mode = 0; + pVMR9->rtLastStop = -1; + + pVMR9->ack = CreateEventW(NULL, 0, 0, NULL); + /* construct input pin */ + piInput.dir = PINDIR_INPUT; + piInput.pFilter = (IBaseFilter *)pVMR9; + lstrcpynW(piInput.achName, wcsInputPinName, sizeof(piInput.achName) / sizeof(piInput.achName[0])); + + hr = InputPin_Construct(&VMR9_InputPin_Vtbl, &piInput, VMR9_Sample, (LPVOID)pVMR9, VMR9_QueryAccept, NULL, &pVMR9->csFilter, NULL, (IPin **)&pVMR9->pInputPin); + + if (SUCCEEDED(hr)) + { + MediaSeekingImpl_Init((IBaseFilter*)pVMR9, VMR9Impl_Change, VMR9Impl_Change, VMR9Impl_Change, &pVMR9->mediaSeeking, &pVMR9->csFilter); + pVMR9->mediaSeeking.lpVtbl = &VMR9_MediaSeeking_Vtbl; + + *ppv = (LPVOID)pVMR9; + TRACE("Created at %p\n", pVMR9); + pVMR9->state_handle = CreateEventW(NULL, TRUE, TRUE, NULL); + pVMR9->blocked = CreateEventW(NULL, FALSE, FALSE, NULL); + } + else + { + pVMR9->csFilter.DebugInfo->Spare[0] = 0; + DeleteCriticalSection(&pVMR9->csFilter); + CoTaskMemFree(pVMR9); + } + + return hr; +} + +static void VMR9Impl_destroy(VMR9Impl *This) +{ + IPin *pConnectedTo; + + TRACE("Destroying\n"); + if (This->pClock) + IReferenceClock_Release(This->pClock); + + if (SUCCEEDED(IPin_ConnectedTo((IPin *)This->pInputPin, &pConnectedTo))) + { + IPin_Disconnect(pConnectedTo); + IPin_Release(pConnectedTo); + } + IPin_Disconnect((IPin *)This->pInputPin); + IPin_Release((IPin *)This->pInputPin); + + This->csFilter.DebugInfo->Spare[0] = 0; + DeleteCriticalSection(&This->csFilter); + + CloseHandle(This->ack); + CloseHandle(This->state_handle); + CloseHandle(This->blocked); + + ZeroMemory(This, sizeof(*This)); + CoTaskMemFree(This); +} + +static HRESULT WINAPI VMR9Inner_QueryInterface(IUnknown * iface, REFIID riid, LPVOID * ppv) +{ + ICOM_THIS_MULTI(VMR9Impl, IInner_vtbl, iface); + TRACE("(%p/%p)->(%s, %p)\n", This, iface, qzdebugstr_guid(riid), ppv); + + if (This->bAggregatable) + This->bUnkOuterValid = TRUE; + + *ppv = NULL; + + if (IsEqualIID(riid, &IID_IUnknown)) + *ppv = (LPVOID)&(This->IInner_vtbl); + 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; + else if (IsEqualIID(riid, &IID_IMediaSeeking)) + *ppv = &This->mediaSeeking; + + if (*ppv) + { + IUnknown_AddRef((IUnknown *)(*ppv)); + return S_OK; + } + + if (IsEqualIID(riid, &IID_IPin)) + ; + else if (IsEqualIID(riid, &IID_IBasicVideo)) + FIXME("No interface for IID_IBasicVideo\n"); + else if (IsEqualIID(riid, &IID_IBasicVideo2)) + FIXME("No interface for IID_IBasicVideo2\n"); + else if (IsEqualIID(riid, &IID_IVideoWindow)) + FIXME("No interface for IID_IVideoWindow\n"); + else if (IsEqualIID(riid, &IID_IVMRWindowlessControl9)) + ; + else if (IsEqualIID(riid, &IID_IVMRSurfaceAllocatorNotify9)) + ; + else if (IsEqualIID(riid, &IID_IAMFilterMiscFlags)) + FIXME("No interface for IID_IAMFilterMiscFlags\n"); + else if (IsEqualIID(riid, &IID_IMediaPosition)) + FIXME("No interface for IID_IMediaPosition\n"); + else if (IsEqualIID(riid, &IID_IQualityControl)) + FIXME("No interface for IID_IQualityControl\n"); + else if (IsEqualIID(riid, &IID_IQualProp)) + FIXME("No interface for IID_IQualProp\n"); + else if (IsEqualIID(riid, &IID_IVMRAspectRatioControl9)) + FIXME("No interface for IID_IVMRAspectRatioControl9\n"); + else if (IsEqualIID(riid, &IID_IVMRDeinterlaceControl9)) + FIXME("No interface for IID_IVMRDeinterlaceControl9\n"); + else if (IsEqualIID(riid, &IID_IVMRMixerBitmap9)) + FIXME("No interface for IID_IVMRMixerBitmap9\n"); + else if (IsEqualIID(riid, &IID_IVMRMonitorConfig9)) + FIXME("No interface for IID_IVMRMonitorConfig9\n"); + else if (IsEqualIID(riid, &IID_IVMRMixerControl9)) + FIXME("No interface for IID_IVMRMixerControl9\n"); + else + FIXME("No interface for %s\n", debugstr_guid(riid)); + + return E_NOINTERFACE; +} + +static ULONG WINAPI VMR9Inner_AddRef(IUnknown * iface) +{ + ICOM_THIS_MULTI(VMR9Impl, IInner_vtbl, iface); + ULONG refCount = InterlockedIncrement(&This->ref); + + TRACE("(%p/%p)->() AddRef from %d\n", This, iface, refCount - 1); + + return refCount; +} + +static ULONG WINAPI VMR9Inner_Release(IUnknown * iface) +{ + ICOM_THIS_MULTI(VMR9Impl, IInner_vtbl, iface); + ULONG refCount = InterlockedDecrement(&This->ref); + + TRACE("(%p/%p)->() Release from %d\n", This, iface, refCount + 1); + + if (refCount) + return refCount; + + VMR9Impl_destroy(This); + return 0; +} + +static const IUnknownVtbl IInner_VTable = +{ + VMR9Inner_QueryInterface, + VMR9Inner_AddRef, + VMR9Inner_Release +}; + +static HRESULT WINAPI VMR9_QueryInterface(IBaseFilter * iface, REFIID riid, LPVOID * ppv) +{ + VMR9Impl *This = (VMR9Impl *)iface; + + if (This->bAggregatable) + This->bUnkOuterValid = TRUE; + + if (This->pUnkOuter) + { + if (This->bAggregatable) + return IUnknown_QueryInterface(This->pUnkOuter, riid, ppv); + + if (IsEqualIID(riid, &IID_IUnknown)) + { + HRESULT hr; + + IUnknown_AddRef((IUnknown *)&(This->IInner_vtbl)); + hr = IUnknown_QueryInterface((IUnknown *)&(This->IInner_vtbl), riid, ppv); + IUnknown_Release((IUnknown *)&(This->IInner_vtbl)); + This->bAggregatable = TRUE; + return hr; + } + + *ppv = NULL; + return E_NOINTERFACE; + } + + return IUnknown_QueryInterface((IUnknown *)&(This->IInner_vtbl), riid, ppv); +} + +static ULONG WINAPI VMR9_AddRef(IBaseFilter * iface) +{ + VMR9Impl *This = (VMR9Impl *)iface; + LONG ret; + + if (This->pUnkOuter && This->bUnkOuterValid) + ret = IUnknown_AddRef(This->pUnkOuter); + else + ret = IUnknown_AddRef((IUnknown *)&(This->IInner_vtbl)); + + TRACE("(%p)->AddRef from %d\n", iface, ret - 1); + + return ret; +} + +static ULONG WINAPI VMR9_Release(IBaseFilter * iface) +{ + VMR9Impl *This = (VMR9Impl *)iface; + LONG ret; + + if (This->pUnkOuter && This->bUnkOuterValid) + ret = IUnknown_Release(This->pUnkOuter); + else + ret = IUnknown_Release((IUnknown *)&(This->IInner_vtbl)); + + TRACE("(%p)->Release from %d\n", iface, ret + 1); + + if (ret) + return ret; + return 0; +} + +/** IPersist methods **/ + +static HRESULT WINAPI VMR9_GetClassID(IBaseFilter * iface, CLSID * pClsid) +{ + VMR9Impl *This = (VMR9Impl *)iface; + + TRACE("(%p/%p)->(%p)\n", This, iface, pClsid); + + *pClsid = CLSID_VideoMixingRenderer9; + + return S_OK; +} + +/** IMediaFilter methods **/ + +static HRESULT WINAPI VMR9_Stop(IBaseFilter * iface) +{ + VMR9Impl *This = (VMR9Impl *)iface; + + TRACE("(%p/%p)->()\n", This, iface); + + EnterCriticalSection(&This->csFilter); + { + This->state = State_Stopped; + SetEvent(This->state_handle); + SetEvent(This->blocked); + } + LeaveCriticalSection(&This->csFilter); + + return S_OK; +} + +static HRESULT WINAPI VMR9_Pause(IBaseFilter * iface) +{ + VMR9Impl *This = (VMR9Impl *)iface; + HRESULT hr = S_OK; + + TRACE("(%p/%p)->()\n", This, iface); + + EnterCriticalSection(&This->csFilter); + { + if (This->state == State_Stopped) + { + This->pInputPin->end_of_stream = 0; + if (SUCCEEDED(hr)) + ResetEvent(This->state_handle); + } + if (SUCCEEDED(hr)) + { + This->state = State_Paused; + ResetEvent(This->blocked); + } + } + LeaveCriticalSection(&This->csFilter); + + return hr; +} + +static HRESULT WINAPI VMR9_Run(IBaseFilter * iface, REFERENCE_TIME tStart) +{ + VMR9Impl *This = (VMR9Impl *)iface; + HRESULT hr = S_OK; + + TRACE("(%p/%p)->(%s)\n", This, iface, wine_dbgstr_longlong(tStart)); + + EnterCriticalSection(&This->csFilter); + if (SUCCEEDED(hr)) + { + This->rtStreamStart = tStart; + if (This->state != State_Running) + { + if (This->state == State_Stopped) + ResetEvent(This->state_handle); + + /* Kick back to live if paused */ + SetEvent(This->blocked); + } + + if (SUCCEEDED(hr)) + This->state = State_Running; + This->pInputPin->end_of_stream = 0; + } + LeaveCriticalSection(&This->csFilter); + + return hr; +} + +static HRESULT WINAPI VMR9_GetState(IBaseFilter * iface, DWORD dwMilliSecsTimeout, FILTER_STATE *pState) +{ + VMR9Impl *This = (VMR9Impl *)iface; + HRESULT hr; + + TRACE("(%p/%p)->(%d, %p)\n", This, iface, dwMilliSecsTimeout, pState); + + if (WaitForSingleObject(This->state_handle, dwMilliSecsTimeout) == WAIT_TIMEOUT) + hr = VFW_S_STATE_INTERMEDIATE; + else + hr = S_OK; + EnterCriticalSection(&This->csFilter); + { + *pState = This->state; + } + LeaveCriticalSection(&This->csFilter); + + return hr; +} + +static HRESULT WINAPI VMR9_SetSyncSource(IBaseFilter * iface, IReferenceClock *pClock) +{ + VMR9Impl *This = (VMR9Impl *)iface; + + TRACE("(%p/%p)->(%p)\n", This, iface, pClock); + + 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 VMR9_GetSyncSource(IBaseFilter * iface, IReferenceClock **ppClock) +{ + VMR9Impl *This = (VMR9Impl *)iface; + + TRACE("(%p/%p)->(%p)\n", This, iface, ppClock); + + EnterCriticalSection(&This->csFilter); + { + *ppClock = This->pClock; + if (This->pClock) + IReferenceClock_AddRef(This->pClock); + } + LeaveCriticalSection(&This->csFilter); + + return S_OK; +} + +/** IBaseFilter implementation **/ + +static HRESULT VMR9_GetPin(IBaseFilter *iface, ULONG pos, IPin **pin, DWORD *lastsynctick) +{ + VMR9Impl *This = (VMR9Impl *)iface; + + /* Our pins are static for all its intents and purposes, so not changing tick count is ok */ + *lastsynctick = 0; + + if (pos >= 1) + return S_FALSE; + + *pin = (IPin *)This->pInputPin; + IPin_AddRef(*pin); + return S_OK; +} + +static HRESULT WINAPI VMR9_EnumPins(IBaseFilter * iface, IEnumPins **ppEnum) +{ + VMR9Impl *This = (VMR9Impl *)iface; + + TRACE("(%p/%p)->(%p)\n", This, iface, ppEnum); + + return IEnumPinsImpl_Construct(ppEnum, VMR9_GetPin, iface); +} + +static HRESULT WINAPI VMR9_FindPin(IBaseFilter * iface, LPCWSTR Id, IPin **ppPin) +{ + VMR9Impl *This = (VMR9Impl *)iface; + + TRACE("(%p/%p)->(%p,%p)\n", This, iface, debugstr_w(Id), ppPin); + + FIXME("VMR9::FindPin(...)\n"); + + /* FIXME: critical section */ + + return E_NOTIMPL; +} + +static HRESULT WINAPI VMR9_QueryFilterInfo(IBaseFilter * iface, FILTER_INFO *pInfo) +{ + VMR9Impl *This = (VMR9Impl *)iface; + + TRACE("(%p/%p)->(%p)\n", This, iface, pInfo); + + strcpyW(pInfo->achName, This->filterInfo.achName); + pInfo->pGraph = This->filterInfo.pGraph; + + if (pInfo->pGraph) + IFilterGraph_AddRef(pInfo->pGraph); + + return S_OK; +} + +static HRESULT WINAPI VMR9_JoinFilterGraph(IBaseFilter * iface, IFilterGraph *pGraph, LPCWSTR pName) +{ + VMR9Impl *This = (VMR9Impl *)iface; + + TRACE("(%p/%p)->(%p, %s)\n", This, iface, pGraph, debugstr_w(pName)); + + EnterCriticalSection(&This->csFilter); + { + if (pName) + strcpyW(This->filterInfo.achName, pName); + else + *This->filterInfo.achName = '\0'; + This->filterInfo.pGraph = pGraph; /* NOTE: do NOT increase ref. count */ + } + LeaveCriticalSection(&This->csFilter); + + return S_OK; +} + +static HRESULT WINAPI VMR9_QueryVendorInfo(IBaseFilter * iface, LPWSTR *pVendorInfo) +{ + VMR9Impl *This = (VMR9Impl *)iface; + TRACE("(%p/%p)->(%p)\n", This, iface, pVendorInfo); + return E_NOTIMPL; +} + +static const IBaseFilterVtbl VMR9_Vtbl = +{ + VMR9_QueryInterface, + VMR9_AddRef, + VMR9_Release, + VMR9_GetClassID, + VMR9_Stop, + VMR9_Pause, + VMR9_Run, + VMR9_GetState, + VMR9_SetSyncSource, + VMR9_GetSyncSource, + VMR9_EnumPins, + VMR9_FindPin, + VMR9_QueryFilterInfo, + VMR9_JoinFilterGraph, + VMR9_QueryVendorInfo +}; + +static HRESULT WINAPI VMR9_InputPin_EndOfStream(IPin * iface) +{ + InputPin* This = (InputPin*)iface; + IMediaEventSink* pEventSink; + HRESULT hr; + + TRACE("(%p/%p)->()\n", This, iface); + + InputPin_EndOfStream(iface); + hr = IFilterGraph_QueryInterface(((VMR9Impl*)This->pin.pinInfo.pFilter)->filterInfo.pGraph, &IID_IMediaEventSink, (LPVOID*)&pEventSink); + if (SUCCEEDED(hr)) + { + hr = IMediaEventSink_Notify(pEventSink, EC_COMPLETE, S_OK, 0); + IMediaEventSink_Release(pEventSink); + } + + return hr; +} + +static HRESULT WINAPI VMR9_InputPin_ReceiveConnection(IPin * iface, IPin * pReceivePin, const AM_MEDIA_TYPE * pmt) +{ + InputPin *This = (InputPin *)iface; + HRESULT hr = S_OK; + + TRACE("()\n"); + + EnterCriticalSection(This->pin.pCritSec); + + if (SUCCEEDED(hr)) + { + hr = InputPin_ReceiveConnection(iface, pReceivePin, pmt); + if (SUCCEEDED(hr)) + assert(IsEqualIID(&This->pin.mtCurrent.majortype, &MEDIATYPE_Video)); + + if (FAILED(hr) && hr != VFW_E_ALREADY_CONNECTED) + IPin_Disconnect(iface); + } + + LeaveCriticalSection(This->pin.pCritSec); + + return hr; +} + +static HRESULT WINAPI VMR9_InputPin_Disconnect(IPin * iface) +{ + InputPin *This = (InputPin *)iface; + HRESULT hr = S_OK; + + TRACE("()\n"); + + EnterCriticalSection(This->pin.pCritSec); + if (!This->pin.pConnectedTo) + { + hr = S_FALSE; + } + if (This->pin.pConnectedTo) + { + IPin_Release(This->pin.pConnectedTo); + This->pin.pConnectedTo = NULL; + } + LeaveCriticalSection(This->pin.pCritSec); + + return hr; +} + +static HRESULT WINAPI VMR9_InputPin_BeginFlush(IPin * iface) +{ + InputPin* This = (InputPin*)iface; + VMR9Impl *pVMR9 = (VMR9Impl *)This->pin.pinInfo.pFilter; + HRESULT hr; + + TRACE("(%p/%p)->()\n", This, iface); + + EnterCriticalSection(This->pin.pCritSec); + if (pVMR9->state == State_Paused) + SetEvent(pVMR9->blocked); + + hr = InputPin_BeginFlush(iface); + LeaveCriticalSection(This->pin.pCritSec); + + return hr; +} + +static HRESULT WINAPI VMR9_InputPin_EndFlush(IPin * iface) +{ + InputPin* This = (InputPin*)iface; + VMR9Impl *pVMR9 = (VMR9Impl *)This->pin.pinInfo.pFilter; + HRESULT hr; + + TRACE("(%p/%p)->()\n", This, iface); + + EnterCriticalSection(This->pin.pCritSec); + if (pVMR9->state == State_Paused) + ResetEvent(pVMR9->blocked); + + hr = InputPin_EndFlush(iface); + LeaveCriticalSection(This->pin.pCritSec); + + return hr; +} + +static const IPinVtbl VMR9_InputPin_Vtbl = +{ + InputPin_QueryInterface, + IPinImpl_AddRef, + InputPin_Release, + InputPin_Connect, + VMR9_InputPin_ReceiveConnection, + VMR9_InputPin_Disconnect, + IPinImpl_ConnectedTo, + IPinImpl_ConnectionMediaType, + IPinImpl_QueryPinInfo, + IPinImpl_QueryDirection, + IPinImpl_QueryId, + IPinImpl_QueryAccept, + IPinImpl_EnumMediaTypes, + IPinImpl_QueryInternalConnections, + VMR9_InputPin_EndOfStream, + VMR9_InputPin_BeginFlush, + VMR9_InputPin_EndFlush, + InputPin_NewSegment +}; -- 1.5.4.1