quartz: Implement a dummy null renderer for directshow.
diff --git a/dlls/quartz/Makefile.in b/dlls/quartz/Makefile.in
index e183f20..207ea1e 100644
--- a/dlls/quartz/Makefile.in
+++ b/dlls/quartz/Makefile.in
@@ -24,6 +24,7 @@
 	main.c \
 	memallocator.c \
 	mpegsplit.c \
+	nullrenderer.c \
 	parser.c \
 	pin.c \
 	regsvr.c \
diff --git a/dlls/quartz/main.c b/dlls/quartz/main.c
index 73c5062..fa08d73 100644
--- a/dlls/quartz/main.c
+++ b/dlls/quartz/main.c
@@ -69,6 +69,7 @@
     { &CLSID_AviSplitter, AVISplitter_create },
     { &CLSID_MPEG1Splitter, MPEGSplitter_create },
     { &CLSID_VideoRenderer, VideoRenderer_create },
+    { &CLSID_NullRenderer, NullRenderer_create },
     { &CLSID_VideoRendererDefault, VideoRendererDefault_create },
     { &CLSID_DSoundRender, DSoundRender_create },
     { &CLSID_AVIDec, AVIDec_create },
diff --git a/dlls/quartz/nullrenderer.c b/dlls/quartz/nullrenderer.c
new file mode 100644
index 0000000..2f663a4
--- /dev/null
+++ b/dlls/quartz/nullrenderer.c
@@ -0,0 +1,564 @@
+/*
+ * Null Renderer (Promiscuous, not rendering anything at all!)
+ *
+ * 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"
+
+#define NONAMELESSSTRUCT
+#define NONAMELESSUNION
+#include "quartz_private.h"
+#include "control_private.h"
+#include "pin.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 "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 NullRenderer_Vtbl;
+static const IUnknownVtbl IInner_VTable;
+static const IPinVtbl NullRenderer_InputPin_Vtbl;
+
+typedef struct NullRendererImpl
+{
+    const IBaseFilterVtbl * lpVtbl;
+    const IUnknownVtbl * IInner_vtbl;
+
+    LONG refCount;
+    CRITICAL_SECTION csFilter;
+    FILTER_STATE state;
+    REFERENCE_TIME rtStreamStart;
+    IReferenceClock * pClock;
+    FILTER_INFO filterInfo;
+
+    InputPin * pInputPin;
+    IPin ** ppPins;
+    IUnknown * pUnkOuter;
+    BOOL bUnkOuterValid;
+    BOOL bAggregatable;
+} NullRendererImpl;
+
+static const IMemInputPinVtbl MemInputPin_Vtbl =
+{
+    MemInputPin_QueryInterface,
+    MemInputPin_AddRef,
+    MemInputPin_Release,
+    MemInputPin_GetAllocator,
+    MemInputPin_NotifyAllocator,
+    MemInputPin_GetAllocatorRequirements,
+    MemInputPin_Receive,
+    MemInputPin_ReceiveMultiple,
+    MemInputPin_ReceiveCanBlock
+};
+
+static HRESULT NullRenderer_InputPin_Construct(const PIN_INFO * pPinInfo, SAMPLEPROC pSampleProc, LPVOID pUserData, QUERYACCEPTPROC pQueryAccept, LPCRITICAL_SECTION pCritSec, IPin ** ppPin)
+{
+    InputPin * pPinImpl;
+
+    *ppPin = NULL;
+
+    if (pPinInfo->dir != PINDIR_INPUT)
+    {
+        ERR("Pin direction(%x) != PINDIR_INPUT\n", pPinInfo->dir);
+        return E_INVALIDARG;
+    }
+
+    pPinImpl = CoTaskMemAlloc(sizeof(*pPinImpl));
+
+    if (!pPinImpl)
+        return E_OUTOFMEMORY;
+
+    if (SUCCEEDED(InputPin_Init(pPinInfo, pSampleProc, pUserData, pQueryAccept, pCritSec, pPinImpl)))
+    {
+        pPinImpl->pin.lpVtbl = &NullRenderer_InputPin_Vtbl;
+        pPinImpl->lpVtblMemInput = &MemInputPin_Vtbl;
+
+        *ppPin = (IPin *)(&pPinImpl->pin.lpVtbl);
+        return S_OK;
+    }
+
+    CoTaskMemFree(pPinImpl);
+    return E_FAIL;
+}
+
+
+
+static HRESULT NullRenderer_Sample(LPVOID iface, IMediaSample * pSample)
+{
+    LPBYTE pbSrcStream = NULL;
+    long cbSrcStream = 0;
+    REFERENCE_TIME tStart, tStop;
+    HRESULT hr;
+
+    TRACE("%p %p\n", iface, pSample);
+
+    hr = IMediaSample_GetPointer(pSample, &pbSrcStream);
+    if (FAILED(hr))
+    {
+        ERR("Cannot get pointer to sample data (%x)\n", hr);
+        return hr;
+    }
+
+    hr = IMediaSample_GetTime(pSample, &tStart, &tStop);
+    if (FAILED(hr))
+        ERR("Cannot get sample time (%x)\n", hr);
+
+    cbSrcStream = IMediaSample_GetActualDataLength(pSample);
+
+    TRACE("val %p %ld\n", pbSrcStream, cbSrcStream);
+
+    return S_OK;
+}
+
+static HRESULT NullRenderer_QueryAccept(LPVOID iface, const AM_MEDIA_TYPE * pmt)
+{
+    TRACE("Not a stub!\n");
+    return S_OK;
+}
+
+HRESULT NullRenderer_create(IUnknown * pUnkOuter, LPVOID * ppv)
+{
+    HRESULT hr;
+    PIN_INFO piInput;
+    NullRendererImpl * pNullRenderer;
+
+    TRACE("(%p, %p)\n", pUnkOuter, ppv);
+
+    *ppv = NULL;
+
+    pNullRenderer = CoTaskMemAlloc(sizeof(NullRendererImpl));
+    pNullRenderer->pUnkOuter = pUnkOuter;
+    pNullRenderer->bUnkOuterValid = FALSE;
+    pNullRenderer->bAggregatable = FALSE;
+    pNullRenderer->IInner_vtbl = &IInner_VTable;
+
+    pNullRenderer->lpVtbl = &NullRenderer_Vtbl;
+    pNullRenderer->refCount = 1;
+    InitializeCriticalSection(&pNullRenderer->csFilter);
+    pNullRenderer->csFilter.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": NullRendererImpl.csFilter");
+    pNullRenderer->state = State_Stopped;
+    pNullRenderer->pClock = NULL;
+    ZeroMemory(&pNullRenderer->filterInfo, sizeof(FILTER_INFO));
+
+    pNullRenderer->ppPins = CoTaskMemAlloc(1 * sizeof(IPin *));
+
+    /* construct input pin */
+    piInput.dir = PINDIR_INPUT;
+    piInput.pFilter = (IBaseFilter *)pNullRenderer;
+    lstrcpynW(piInput.achName, wcsInputPinName, sizeof(piInput.achName) / sizeof(piInput.achName[0]));
+
+    hr = NullRenderer_InputPin_Construct(&piInput, NullRenderer_Sample, (LPVOID)pNullRenderer, NullRenderer_QueryAccept, &pNullRenderer->csFilter, (IPin **)&pNullRenderer->pInputPin);
+
+    if (SUCCEEDED(hr))
+    {
+        pNullRenderer->ppPins[0] = (IPin *)pNullRenderer->pInputPin;
+        *ppv = (LPVOID)pNullRenderer;
+    }
+    else
+    {
+        CoTaskMemFree(pNullRenderer->ppPins);
+        pNullRenderer->csFilter.DebugInfo->Spare[0] = 0;
+        DeleteCriticalSection(&pNullRenderer->csFilter);
+        CoTaskMemFree(pNullRenderer);
+    }
+
+    return hr;
+}
+
+static HRESULT WINAPI NullRendererInner_QueryInterface(IUnknown * iface, REFIID riid, LPVOID * ppv)
+{
+    ICOM_THIS_MULTI(NullRendererImpl, 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;
+
+    if (*ppv)
+    {
+        IUnknown_AddRef((IUnknown *)(*ppv));
+        return S_OK;
+    }
+
+    FIXME("No interface for %s!\n", qzdebugstr_guid(riid));
+
+    return E_NOINTERFACE;
+}
+
+static ULONG WINAPI NullRendererInner_AddRef(IUnknown * iface)
+{
+    ICOM_THIS_MULTI(NullRendererImpl, IInner_vtbl, iface);
+    ULONG refCount = InterlockedIncrement(&This->refCount);
+
+    TRACE("(%p/%p)->() AddRef from %d\n", This, iface, refCount - 1);
+
+    return refCount;
+}
+
+static ULONG WINAPI NullRendererInner_Release(IUnknown * iface)
+{
+    ICOM_THIS_MULTI(NullRendererImpl, IInner_vtbl, iface);
+    ULONG refCount = InterlockedDecrement(&This->refCount);
+
+    TRACE("(%p/%p)->() Release from %d\n", This, iface, refCount + 1);
+
+    if (!refCount)
+    {
+        IPin *pConnectedTo;
+
+        if (This->pClock)
+            IReferenceClock_Release(This->pClock);
+
+        if (SUCCEEDED(IPin_ConnectedTo(This->ppPins[0], &pConnectedTo)))
+        {
+            IPin_Disconnect(pConnectedTo);
+            IPin_Release(pConnectedTo);
+        }
+        IPin_Disconnect(This->ppPins[0]);
+        IPin_Release(This->ppPins[0]);
+
+        CoTaskMemFree(This->ppPins);
+        This->lpVtbl = NULL;
+
+        This->csFilter.DebugInfo->Spare[0] = 0;
+        DeleteCriticalSection(&This->csFilter);
+
+        TRACE("Destroying Null Renderer\n");
+        CoTaskMemFree(This);
+        return 0;
+    }
+    else
+        return refCount;
+}
+
+static const IUnknownVtbl IInner_VTable =
+{
+    NullRendererInner_QueryInterface,
+    NullRendererInner_AddRef,
+    NullRendererInner_Release
+};
+
+static HRESULT WINAPI NullRenderer_QueryInterface(IBaseFilter * iface, REFIID riid, LPVOID * ppv)
+{
+    NullRendererImpl *This = (NullRendererImpl *)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 NullRenderer_AddRef(IBaseFilter * iface)
+{
+    NullRendererImpl *This = (NullRendererImpl *)iface;
+
+    if (This->pUnkOuter && This->bUnkOuterValid)
+        return IUnknown_AddRef(This->pUnkOuter);
+    return IUnknown_AddRef((IUnknown *)&(This->IInner_vtbl));
+}
+
+static ULONG WINAPI NullRenderer_Release(IBaseFilter * iface)
+{
+    NullRendererImpl *This = (NullRendererImpl *)iface;
+
+    if (This->pUnkOuter && This->bUnkOuterValid)
+        return IUnknown_Release(This->pUnkOuter);
+    return IUnknown_Release((IUnknown *)&(This->IInner_vtbl));
+}
+
+/** IPersist methods **/
+
+static HRESULT WINAPI NullRenderer_GetClassID(IBaseFilter * iface, CLSID * pClsid)
+{
+    NullRendererImpl *This = (NullRendererImpl *)iface;
+
+    TRACE("(%p/%p)->(%p)\n", This, iface, pClsid);
+
+    *pClsid = CLSID_NullRenderer;
+
+    return S_OK;
+}
+
+/** IMediaFilter methods **/
+
+static HRESULT WINAPI NullRenderer_Stop(IBaseFilter * iface)
+{
+    NullRendererImpl *This = (NullRendererImpl *)iface;
+
+    TRACE("(%p/%p)->()\n", This, iface);
+
+    EnterCriticalSection(&This->csFilter);
+    {
+        This->state = State_Stopped;
+    }
+    LeaveCriticalSection(&This->csFilter);
+
+    return S_OK;
+}
+
+static HRESULT WINAPI NullRenderer_Pause(IBaseFilter * iface)
+{
+    NullRendererImpl *This = (NullRendererImpl *)iface;
+
+    TRACE("(%p/%p)->()\n", This, iface);
+
+    EnterCriticalSection(&This->csFilter);
+    {
+        This->state = State_Paused;
+    }
+    LeaveCriticalSection(&This->csFilter);
+
+    return S_OK;
+}
+
+static HRESULT WINAPI NullRenderer_Run(IBaseFilter * iface, REFERENCE_TIME tStart)
+{
+    NullRendererImpl *This = (NullRendererImpl *)iface;
+
+    TRACE("(%p/%p)->(%s)\n", This, iface, wine_dbgstr_longlong(tStart));
+
+    EnterCriticalSection(&This->csFilter);
+    {
+        This->rtStreamStart = tStart;
+        This->state = State_Running;
+    }
+    LeaveCriticalSection(&This->csFilter);
+
+    return S_OK;
+}
+
+static HRESULT WINAPI NullRenderer_GetState(IBaseFilter * iface, DWORD dwMilliSecsTimeout, FILTER_STATE *pState)
+{
+    NullRendererImpl *This = (NullRendererImpl *)iface;
+
+    TRACE("(%p/%p)->(%d, %p)\n", This, iface, dwMilliSecsTimeout, pState);
+
+    EnterCriticalSection(&This->csFilter);
+    {
+        *pState = This->state;
+    }
+    LeaveCriticalSection(&This->csFilter);
+
+    return S_OK;
+}
+
+static HRESULT WINAPI NullRenderer_SetSyncSource(IBaseFilter * iface, IReferenceClock *pClock)
+{
+    NullRendererImpl *This = (NullRendererImpl *)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 NullRenderer_GetSyncSource(IBaseFilter * iface, IReferenceClock **ppClock)
+{
+    NullRendererImpl *This = (NullRendererImpl *)iface;
+
+    TRACE("(%p/%p)->(%p)\n", This, iface, ppClock);
+
+    EnterCriticalSection(&This->csFilter);
+    {
+        *ppClock = This->pClock;
+        IReferenceClock_AddRef(This->pClock);
+    }
+    LeaveCriticalSection(&This->csFilter);
+
+    return S_OK;
+}
+
+/** IBaseFilter implementation **/
+
+static HRESULT WINAPI NullRenderer_EnumPins(IBaseFilter * iface, IEnumPins **ppEnum)
+{
+    ENUMPINDETAILS epd;
+    NullRendererImpl *This = (NullRendererImpl *)iface;
+
+    TRACE("(%p/%p)->(%p)\n", This, iface, ppEnum);
+
+    epd.cPins = 1; /* input pin */
+    epd.ppPins = This->ppPins;
+    return IEnumPinsImpl_Construct(&epd, ppEnum);
+}
+
+static HRESULT WINAPI NullRenderer_FindPin(IBaseFilter * iface, LPCWSTR Id, IPin **ppPin)
+{
+    NullRendererImpl *This = (NullRendererImpl *)iface;
+
+    TRACE("(%p/%p)->(%p,%p)\n", This, iface, debugstr_w(Id), ppPin);
+
+    FIXME("NullRenderer::FindPin(...)\n");
+
+    /* FIXME: critical section */
+
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI NullRenderer_QueryFilterInfo(IBaseFilter * iface, FILTER_INFO *pInfo)
+{
+    NullRendererImpl *This = (NullRendererImpl *)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 NullRenderer_JoinFilterGraph(IBaseFilter * iface, IFilterGraph *pGraph, LPCWSTR pName)
+{
+    NullRendererImpl *This = (NullRendererImpl *)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 NullRenderer_QueryVendorInfo(IBaseFilter * iface, LPWSTR *pVendorInfo)
+{
+    NullRendererImpl *This = (NullRendererImpl *)iface;
+    TRACE("(%p/%p)->(%p)\n", This, iface, pVendorInfo);
+    return E_NOTIMPL;
+}
+
+static const IBaseFilterVtbl NullRenderer_Vtbl =
+{
+    NullRenderer_QueryInterface,
+    NullRenderer_AddRef,
+    NullRenderer_Release,
+    NullRenderer_GetClassID,
+    NullRenderer_Stop,
+    NullRenderer_Pause,
+    NullRenderer_Run,
+    NullRenderer_GetState,
+    NullRenderer_SetSyncSource,
+    NullRenderer_GetSyncSource,
+    NullRenderer_EnumPins,
+    NullRenderer_FindPin,
+    NullRenderer_QueryFilterInfo,
+    NullRenderer_JoinFilterGraph,
+    NullRenderer_QueryVendorInfo
+};
+
+static HRESULT WINAPI NullRenderer_InputPin_EndOfStream(IPin * iface)
+{
+    InputPin* This = (InputPin*)iface;
+    IMediaEventSink* pEventSink;
+    HRESULT hr;
+
+    TRACE("(%p/%p)->()\n", This, iface);
+
+    hr = IFilterGraph_QueryInterface(((NullRendererImpl*)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 const IPinVtbl NullRenderer_InputPin_Vtbl =
+{
+    InputPin_QueryInterface,
+    IPinImpl_AddRef,
+    InputPin_Release,
+    InputPin_Connect,
+    InputPin_ReceiveConnection,
+    IPinImpl_Disconnect,
+    IPinImpl_ConnectedTo,
+    IPinImpl_ConnectionMediaType,
+    IPinImpl_QueryPinInfo,
+    IPinImpl_QueryDirection,
+    IPinImpl_QueryId,
+    IPinImpl_QueryAccept,
+    IPinImpl_EnumMediaTypes,
+    IPinImpl_QueryInternalConnections,
+    NullRenderer_InputPin_EndOfStream,
+    InputPin_BeginFlush,
+    InputPin_EndFlush,
+    InputPin_NewSegment
+};
diff --git a/dlls/quartz/quartz_private.h b/dlls/quartz/quartz_private.h
index ebb6a02..f730876 100644
--- a/dlls/quartz/quartz_private.h
+++ b/dlls/quartz/quartz_private.h
@@ -52,6 +52,7 @@
 HRESULT AVIDec_create(IUnknown * pUnkOuter, LPVOID * ppv);
 HRESULT DSoundRender_create(IUnknown * pUnkOuter, LPVOID * ppv);
 HRESULT VideoRenderer_create(IUnknown * pUnkOuter, LPVOID * ppv);
+HRESULT NullRenderer_create(IUnknown * pUnkOuter, LPVOID * ppv);
 HRESULT VideoRendererDefault_create(IUnknown * pUnkOuter, LPVOID * ppv);
 HRESULT QUARTZ_CreateSystemClock(IUnknown * pUnkOuter, LPVOID * ppv);
 HRESULT ACMWrapper_create(IUnknown * pUnkOuter, LPVOID * ppv);
diff --git a/dlls/quartz/regsvr.c b/dlls/quartz/regsvr.c
index 98fd0bc..b4a304e 100644
--- a/dlls/quartz/regsvr.c
+++ b/dlls/quartz/regsvr.c
@@ -855,6 +855,12 @@
 	"quartz.dll",
 	"Both"
     },
+    {   &CLSID_NullRenderer,
+        "Null Renderer",
+        NULL,
+        "quartz.dll",
+        "Both"
+    },
     {   &CLSID_VideoRenderer,
 	"Video Renderer",
 	NULL,
@@ -1069,6 +1075,18 @@
             { 0xFFFFFFFF },
         }
     },
+    {   &CLSID_NullRenderer,
+        &CLSID_LegacyAmFilterCategory,
+        {'N','u','l','l',' ','R','e','n','d','e','r','e','r',0},
+        0x200000,
+        {   {   REG_PINFLAG_B_RENDERER,
+                {   { &MEDIATYPE_NULL, &GUID_NULL },
+                    { NULL }
+                },
+            },
+            { 0xFFFFFFFF },
+        }
+    },
     {   &CLSID_VideoRenderer,
 	&CLSID_LegacyAmFilterCategory,
 	{'V','i','d','e','o',' ','R','e','n','d','e','r','e','r',0},