* #36737: switch back to svn repo, remove assert in sip_transaction.c
diff --git a/jni/pjproject-android/.svn/pristine/21/21809bf569e567c198b60ff3d9126c1a1fe704ef.svn-base b/jni/pjproject-android/.svn/pristine/21/21809bf569e567c198b60ff3d9126c1a1fe704ef.svn-base
new file mode 100644
index 0000000..282c35d
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/21/21809bf569e567c198b60ff3d9126c1a1fe704ef.svn-base
@@ -0,0 +1,5363 @@
+//------------------------------------------------------------------------------

+// File: AMFilter.cpp

+//

+// Desc: DirectShow base classes - implements class hierarchy for streams

+//       architecture.

+//

+// Copyright (c) 1992-2001 Microsoft Corporation.  All rights reserved.

+//------------------------------------------------------------------------------

+

+

+//=====================================================================

+//=====================================================================

+// The following classes are declared in this header:

+//

+//

+// CBaseMediaFilter            Basic IMediaFilter support (abstract class)

+// CBaseFilter                 Support for IBaseFilter (incl. IMediaFilter)

+// CEnumPins                   Enumerate input and output pins

+// CEnumMediaTypes             Enumerate the preferred pin formats

+// CBasePin                    Abstract base class for IPin interface

+//    CBaseOutputPin           Adds data provider member functions

+//    CBaseInputPin            Implements IMemInputPin interface

+// CMediaSample                Basic transport unit for IMemInputPin

+// CBaseAllocator              General list guff for most allocators

+//    CMemAllocator            Implements memory buffer allocation

+//

+//=====================================================================

+//=====================================================================

+

+#include <pjmedia-videodev/config.h>

+

+#if defined(PJMEDIA_VIDEO_DEV_HAS_DSHOW) && PJMEDIA_VIDEO_DEV_HAS_DSHOW != 0

+

+#include <streams.h>

+#include <strsafe.h>

+

+#ifdef DXMPERF

+#include "dxmperf.h"

+#endif // DXMPERF

+

+

+//=====================================================================

+// Helpers

+//=====================================================================

+STDAPI CreateMemoryAllocator(__deref_out IMemAllocator **ppAllocator)

+{

+    return CoCreateInstance(CLSID_MemoryAllocator,

+                            0,

+                            CLSCTX_INPROC_SERVER,

+                            IID_IMemAllocator,

+                            (void **)ppAllocator);

+}

+

+//  Put this one here rather than in ctlutil.cpp to avoid linking

+//  anything brought in by ctlutil.cpp

+STDAPI CreatePosPassThru(

+    __in_opt LPUNKNOWN pAgg,

+    BOOL bRenderer,

+    IPin *pPin,

+    __deref_out IUnknown **ppPassThru

+)

+{

+    *ppPassThru = NULL;

+    IUnknown *pUnkSeek;

+    HRESULT hr = CoCreateInstance(CLSID_SeekingPassThru,

+                                  pAgg,

+                                  CLSCTX_INPROC_SERVER,

+                                  IID_IUnknown,

+                                  (void **)&pUnkSeek

+                                 );

+    if (FAILED(hr)) {

+        return hr;

+    }

+

+    ISeekingPassThru *pPassThru;

+    hr = pUnkSeek->QueryInterface(IID_ISeekingPassThru, (void**)&pPassThru);

+    if (FAILED(hr)) {

+        pUnkSeek->Release();

+        return hr;

+    }

+    hr = pPassThru->Init(bRenderer, pPin);

+    pPassThru->Release();

+    if (FAILED(hr)) {

+        pUnkSeek->Release();

+        return hr;

+    }

+    *ppPassThru = pUnkSeek;

+    return S_OK;

+}

+

+

+

+#define CONNECT_TRACE_LEVEL 3

+

+//=====================================================================

+//=====================================================================

+// Implements CBaseMediaFilter

+//=====================================================================

+//=====================================================================

+

+

+/* Constructor */

+

+CBaseMediaFilter::CBaseMediaFilter(__in_opt LPCTSTR pName,

+                   __inout_opt LPUNKNOWN    pUnk,

+                   __in CCritSec *pLock,

+                   REFCLSID clsid) :

+    CUnknown(pName, pUnk),

+    m_pLock(pLock),

+    m_clsid(clsid),

+    m_State(State_Stopped),

+    m_pClock(NULL)

+{

+}

+

+

+/* Destructor */

+

+CBaseMediaFilter::~CBaseMediaFilter()

+{

+    // must be stopped, but can't call Stop here since

+    // our critsec has been destroyed.

+

+    /* Release any clock we were using */

+

+    if (m_pClock) {

+        m_pClock->Release();

+        m_pClock = NULL;

+    }

+}

+

+

+/* Override this to say what interfaces we support and where */

+

+STDMETHODIMP

+CBaseMediaFilter::NonDelegatingQueryInterface(

+    REFIID riid,

+    __deref_out void ** ppv)

+{

+    if (riid == IID_IMediaFilter) {

+        return GetInterface((IMediaFilter *) this, ppv);

+    } else if (riid == IID_IPersist) {

+        return GetInterface((IPersist *) this, ppv);

+    } else {

+        return CUnknown::NonDelegatingQueryInterface(riid, ppv);

+    }

+}

+

+/* Return the filter's clsid */

+STDMETHODIMP

+CBaseMediaFilter::GetClassID(__out CLSID *pClsID)

+{

+    CheckPointer(pClsID,E_POINTER);

+    ValidateReadWritePtr(pClsID,sizeof(CLSID));

+    *pClsID = m_clsid;

+    return NOERROR;

+}

+

+/* Override this if your state changes are not done synchronously */

+

+STDMETHODIMP

+CBaseMediaFilter::GetState(DWORD dwMSecs, __out FILTER_STATE *State)

+{

+    UNREFERENCED_PARAMETER(dwMSecs);

+    CheckPointer(State,E_POINTER);

+    ValidateReadWritePtr(State,sizeof(FILTER_STATE));

+

+    *State = m_State;

+    return S_OK;

+}

+

+

+/* Set the clock we will use for synchronisation */

+

+STDMETHODIMP

+CBaseMediaFilter::SetSyncSource(__inout_opt IReferenceClock *pClock)

+{

+    CAutoLock cObjectLock(m_pLock);

+

+    // Ensure the new one does not go away - even if the same as the old

+    if (pClock) {

+        pClock->AddRef();

+    }

+

+    // if we have a clock, release it

+    if (m_pClock) {

+        m_pClock->Release();

+    }

+

+    // Set the new reference clock (might be NULL)

+    // Should we query it to ensure it is a clock?  Consider for a debug build.

+    m_pClock = pClock;

+

+    return NOERROR;

+}

+

+/* Return the clock we are using for synchronisation */

+STDMETHODIMP

+CBaseMediaFilter::GetSyncSource(__deref_out_opt IReferenceClock **pClock)

+{

+    CheckPointer(pClock,E_POINTER);

+    ValidateReadWritePtr(pClock,sizeof(IReferenceClock *));

+    CAutoLock cObjectLock(m_pLock);

+

+    if (m_pClock) {

+        // returning an interface... addref it...

+        m_pClock->AddRef();

+    }

+    *pClock = (IReferenceClock*)m_pClock;

+    return NOERROR;

+}

+

+

+/* Put the filter into a stopped state */

+

+STDMETHODIMP

+CBaseMediaFilter::Stop()

+{

+    CAutoLock cObjectLock(m_pLock);

+

+    m_State = State_Stopped;

+    return S_OK;

+}

+

+

+/* Put the filter into a paused state */

+

+STDMETHODIMP

+CBaseMediaFilter::Pause()

+{

+    CAutoLock cObjectLock(m_pLock);

+

+    m_State = State_Paused;

+    return S_OK;

+}

+

+

+// Put the filter into a running state.

+

+// The time parameter is the offset to be added to the samples'

+// stream time to get the reference time at which they should be presented.

+//

+// you can either add these two and compare it against the reference clock,

+// or you can call CBaseMediaFilter::StreamTime and compare that against

+// the sample timestamp.

+

+STDMETHODIMP

+CBaseMediaFilter::Run(REFERENCE_TIME tStart)

+{

+    CAutoLock cObjectLock(m_pLock);

+

+    // remember the stream time offset

+    m_tStart = tStart;

+

+    if (m_State == State_Stopped){

+        HRESULT hr = Pause();

+

+        if (FAILED(hr)) {

+            return hr;

+        }

+    }

+    m_State = State_Running;

+    return S_OK;

+}

+

+

+//

+// return the current stream time - samples with start timestamps of this

+// time or before should be rendered by now

+HRESULT

+CBaseMediaFilter::StreamTime(CRefTime& rtStream)

+{

+    // Caller must lock for synchronization

+    // We can't grab the filter lock because we want to be able to call

+    // this from worker threads without deadlocking

+

+    if (m_pClock == NULL) {

+        return VFW_E_NO_CLOCK;

+    }

+

+    // get the current reference time

+    HRESULT hr = m_pClock->GetTime((REFERENCE_TIME*)&rtStream);

+    if (FAILED(hr)) {

+        return hr;

+    }

+

+    // subtract the stream offset to get stream time

+    rtStream -= m_tStart;

+

+    return S_OK;

+}

+

+

+//=====================================================================

+//=====================================================================

+// Implements CBaseFilter

+//=====================================================================

+//=====================================================================

+

+

+/* Override this to say what interfaces we support and where */

+

+STDMETHODIMP CBaseFilter::NonDelegatingQueryInterface(REFIID riid,

+                                                      __deref_out void **ppv)

+{

+    /* Do we have this interface */

+

+    if (riid == IID_IBaseFilter) {

+        return GetInterface((IBaseFilter *) this, ppv);

+    } else if (riid == IID_IMediaFilter) {

+        return GetInterface((IMediaFilter *) this, ppv);

+    } else if (riid == IID_IPersist) {

+        return GetInterface((IPersist *) this, ppv);

+    } else if (riid == IID_IAMovieSetup) {

+        return GetInterface((IAMovieSetup *) this, ppv);

+    } else {

+        return CUnknown::NonDelegatingQueryInterface(riid, ppv);

+    }

+}

+

+#ifdef DEBUG

+STDMETHODIMP_(ULONG) CBaseFilter::NonDelegatingRelease()

+{

+    if (m_cRef == 1) {

+        KASSERT(m_pGraph == NULL);

+    }

+    return CUnknown::NonDelegatingRelease();

+}

+#endif

+

+

+/* Constructor */

+

+CBaseFilter::CBaseFilter(__in_opt LPCTSTR pName,

+             __inout_opt LPUNKNOWN  pUnk,

+             __in CCritSec   *pLock,

+             REFCLSID   clsid) :

+    CUnknown( pName, pUnk ),

+    m_pLock(pLock),

+    m_clsid(clsid),

+    m_State(State_Stopped),

+    m_pClock(NULL),

+    m_pGraph(NULL),

+    m_pSink(NULL),

+    m_pName(NULL),

+    m_PinVersion(1)

+{

+#ifdef DXMPERF

+    PERFLOG_CTOR( pName ? pName : L"CBaseFilter", (IBaseFilter *) this );

+#endif // DXMPERF

+

+    ASSERT(pLock != NULL);

+}

+

+/* Passes in a redundant HRESULT argument */

+

+CBaseFilter::CBaseFilter(__in_opt LPCTSTR pName,

+                         __in_opt LPUNKNOWN  pUnk,

+                         __in CCritSec  *pLock,

+                         REFCLSID   clsid,

+                         __inout HRESULT   *phr) :

+    CUnknown( pName, pUnk ),

+    m_pLock(pLock),

+    m_clsid(clsid),

+    m_State(State_Stopped),

+    m_pClock(NULL),

+    m_pGraph(NULL),

+    m_pSink(NULL),

+    m_pName(NULL),

+    m_PinVersion(1)

+{

+#ifdef DXMPERF

+    PERFLOG_CTOR( pName ? pName : L"CBaseFilter", (IBaseFilter *) this );

+#endif // DXMPERF

+

+    ASSERT(pLock != NULL);

+    UNREFERENCED_PARAMETER(phr);

+}

+

+#ifdef UNICODE

+CBaseFilter::CBaseFilter(__in_opt LPCSTR pName,

+             __in_opt LPUNKNOWN  pUnk,

+             __in CCritSec   *pLock,

+             REFCLSID   clsid) :

+    CUnknown( pName, pUnk ),

+    m_pLock(pLock),

+    m_clsid(clsid),

+    m_State(State_Stopped),

+    m_pClock(NULL),

+    m_pGraph(NULL),

+    m_pSink(NULL),

+    m_pName(NULL),

+    m_PinVersion(1)

+{

+#ifdef DXMPERF

+    PERFLOG_CTOR( L"CBaseFilter", (IBaseFilter *) this );

+#endif // DXMPERF

+

+    ASSERT(pLock != NULL);

+}

+CBaseFilter::CBaseFilter(__in_opt LPCSTR pName,

+                         __in_opt LPUNKNOWN  pUnk,

+                         __in CCritSec  *pLock,

+                         REFCLSID   clsid,

+                         __inout HRESULT   *phr) :

+    CUnknown( pName, pUnk ),

+    m_pLock(pLock),

+    m_clsid(clsid),

+    m_State(State_Stopped),

+    m_pClock(NULL),

+    m_pGraph(NULL),

+    m_pSink(NULL),

+    m_pName(NULL),

+    m_PinVersion(1)

+{

+#ifdef DXMPERF

+    PERFLOG_CTOR( L"CBaseFilter", (IBaseFilter *) this );

+#endif // DXMPERF

+

+    ASSERT(pLock != NULL);

+    UNREFERENCED_PARAMETER(phr);

+}

+#endif

+

+/* Destructor */

+

+CBaseFilter::~CBaseFilter()

+{

+#ifdef DXMPERF

+    PERFLOG_DTOR( L"CBaseFilter", (IBaseFilter *) this );

+#endif // DXMPERF

+

+    // NOTE we do NOT hold references on the filtergraph for m_pGraph or m_pSink

+    // When we did we had the circular reference problem.  Nothing would go away.

+

+    delete[] m_pName;

+

+    // must be stopped, but can't call Stop here since

+    // our critsec has been destroyed.

+

+    /* Release any clock we were using */

+    if (m_pClock) {

+        m_pClock->Release();

+        m_pClock = NULL;

+    }

+}

+

+/* Return the filter's clsid */

+STDMETHODIMP

+CBaseFilter::GetClassID(__out CLSID *pClsID)

+{

+    CheckPointer(pClsID,E_POINTER);

+    ValidateReadWritePtr(pClsID,sizeof(CLSID));

+    *pClsID = m_clsid;

+    return NOERROR;

+}

+

+/* Override this if your state changes are not done synchronously */

+STDMETHODIMP

+CBaseFilter::GetState(DWORD dwMSecs, __out FILTER_STATE *State)

+{

+    UNREFERENCED_PARAMETER(dwMSecs);

+    CheckPointer(State,E_POINTER);

+    ValidateReadWritePtr(State,sizeof(FILTER_STATE));

+

+    *State = m_State;

+    return S_OK;

+}

+

+

+/* Set the clock we will use for synchronisation */

+

+STDMETHODIMP

+CBaseFilter::SetSyncSource(__in_opt IReferenceClock *pClock)

+{

+    CAutoLock cObjectLock(m_pLock);

+

+    // Ensure the new one does not go away - even if the same as the old

+    if (pClock) {

+        pClock->AddRef();

+    }

+

+    // if we have a clock, release it

+    if (m_pClock) {

+        m_pClock->Release();

+    }

+

+    // Set the new reference clock (might be NULL)

+    // Should we query it to ensure it is a clock?  Consider for a debug build.

+    m_pClock = pClock;

+

+    return NOERROR;

+}

+

+/* Return the clock we are using for synchronisation */

+STDMETHODIMP

+CBaseFilter::GetSyncSource(__deref_out_opt IReferenceClock **pClock)

+{

+    CheckPointer(pClock,E_POINTER);

+    ValidateReadWritePtr(pClock,sizeof(IReferenceClock *));

+    CAutoLock cObjectLock(m_pLock);

+

+    if (m_pClock) {

+        // returning an interface... addref it...

+        m_pClock->AddRef();

+    }

+    *pClock = (IReferenceClock*)m_pClock;

+    return NOERROR;

+}

+

+

+

+// override CBaseMediaFilter Stop method, to deactivate any pins this

+// filter has.

+STDMETHODIMP

+CBaseFilter::Stop()

+{

+    CAutoLock cObjectLock(m_pLock);

+    HRESULT hr = NOERROR;

+

+    // notify all pins of the state change

+    if (m_State != State_Stopped) {

+        int cPins = GetPinCount();

+        for (int c = 0; c < cPins; c++) {

+

+            CBasePin *pPin = GetPin(c);

+            if (NULL == pPin) {

+                break;

+            }

+

+            // Disconnected pins are not activated - this saves pins worrying

+            // about this state themselves. We ignore the return code to make

+            // sure everyone is inactivated regardless. The base input pin

+            // class can return an error if it has no allocator but Stop can

+            // be used to resync the graph state after something has gone bad

+

+            if (pPin->IsConnected()) {

+                HRESULT hrTmp = pPin->Inactive();

+                if (FAILED(hrTmp) && SUCCEEDED(hr)) {

+                    hr = hrTmp;

+                }

+            }

+        }

+    }

+

+#ifdef DXMPERF

+    PERFLOG_STOP( m_pName ? m_pName : L"CBaseFilter", (IBaseFilter *) this, m_State );

+#endif // DXMPERF

+

+    m_State = State_Stopped;

+    return hr;

+}

+

+

+// override CBaseMediaFilter Pause method to activate any pins

+// this filter has (also called from Run)

+

+STDMETHODIMP

+CBaseFilter::Pause()

+{

+    CAutoLock cObjectLock(m_pLock);

+

+    // notify all pins of the change to active state

+    if (m_State == State_Stopped) {

+        int cPins = GetPinCount();

+        for (int c = 0; c < cPins; c++) {

+

+            CBasePin *pPin = GetPin(c);

+            if (NULL == pPin) {

+                break;

+            }

+

+            // Disconnected pins are not activated - this saves pins

+            // worrying about this state themselves

+

+            if (pPin->IsConnected()) {

+                HRESULT hr = pPin->Active();

+                if (FAILED(hr)) {

+                    return hr;

+                }

+            }

+        }

+    }

+

+

+#ifdef DXMPERF

+    PERFLOG_PAUSE( m_pName ? m_pName : L"CBaseFilter", (IBaseFilter *) this, m_State );

+#endif // DXMPERF

+

+    m_State = State_Paused;

+    return S_OK;

+}

+

+// Put the filter into a running state.

+

+// The time parameter is the offset to be added to the samples'

+// stream time to get the reference time at which they should be presented.

+//

+// you can either add these two and compare it against the reference clock,

+// or you can call CBaseFilter::StreamTime and compare that against

+// the sample timestamp.

+

+STDMETHODIMP

+CBaseFilter::Run(REFERENCE_TIME tStart)

+{

+    CAutoLock cObjectLock(m_pLock);

+

+    // remember the stream time offset

+    m_tStart = tStart;

+

+    if (m_State == State_Stopped){

+    HRESULT hr = Pause();

+

+    if (FAILED(hr)) {

+        return hr;

+    }

+    }

+    // notify all pins of the change to active state

+    if (m_State != State_Running) {

+        int cPins = GetPinCount();

+        for (int c = 0; c < cPins; c++) {

+

+            CBasePin *pPin = GetPin(c);

+            if (NULL == pPin) {

+                break;

+            }

+

+            // Disconnected pins are not activated - this saves pins

+            // worrying about this state themselves

+

+            if (pPin->IsConnected()) {

+                HRESULT hr = pPin->Run(tStart);

+                if (FAILED(hr)) {

+                    return hr;

+                }

+            }

+        }

+    }

+

+#ifdef DXMPERF

+    PERFLOG_RUN( m_pName ? m_pName : L"CBaseFilter", (IBaseFilter *) this, tStart, m_State );

+#endif // DXMPERF

+

+    m_State = State_Running;

+    return S_OK;

+}

+

+//

+// return the current stream time - samples with start timestamps of this

+// time or before should be rendered by now

+HRESULT

+CBaseFilter::StreamTime(CRefTime& rtStream)

+{

+    // Caller must lock for synchronization

+    // We can't grab the filter lock because we want to be able to call

+    // this from worker threads without deadlocking

+

+    if (m_pClock == NULL) {

+        return VFW_E_NO_CLOCK;

+    }

+

+    // get the current reference time

+    HRESULT hr = m_pClock->GetTime((REFERENCE_TIME*)&rtStream);

+    if (FAILED(hr)) {

+        return hr;

+    }

+

+    // subtract the stream offset to get stream time

+    rtStream -= m_tStart;

+

+    return S_OK;

+}

+

+

+/* Create an enumerator for the pins attached to this filter */

+

+STDMETHODIMP

+CBaseFilter::EnumPins(__deref_out IEnumPins **ppEnum)

+{

+    CheckPointer(ppEnum,E_POINTER);

+    ValidateReadWritePtr(ppEnum,sizeof(IEnumPins *));

+

+    /* Create a new ref counted enumerator */

+

+    *ppEnum = new CEnumPins(this,

+                        NULL);

+

+    return *ppEnum == NULL ? E_OUTOFMEMORY : NOERROR;

+}

+

+

+// default behaviour of FindPin is to assume pins are named

+// by their pin names

+STDMETHODIMP

+CBaseFilter::FindPin(

+    LPCWSTR Id,

+    __deref_out IPin ** ppPin

+)

+{

+    CheckPointer(ppPin,E_POINTER);

+    ValidateReadWritePtr(ppPin,sizeof(IPin *));

+

+    //  We're going to search the pin list so maintain integrity

+    CAutoLock lck(m_pLock);

+    int iCount = GetPinCount();

+    for (int i = 0; i < iCount; i++) {

+        CBasePin *pPin = GetPin(i);

+        if (NULL == pPin) {

+            break;

+        }

+

+        if (0 == lstrcmpW(pPin->Name(), Id)) {

+            //  Found one that matches

+            //

+            //  AddRef() and return it

+            *ppPin = pPin;

+            pPin->AddRef();

+            return S_OK;

+        }

+    }

+    *ppPin = NULL;

+    return VFW_E_NOT_FOUND;

+}

+

+/* Return information about this filter */

+

+STDMETHODIMP

+CBaseFilter::QueryFilterInfo(__out FILTER_INFO * pInfo)

+{

+    CheckPointer(pInfo,E_POINTER);

+    ValidateReadWritePtr(pInfo,sizeof(FILTER_INFO));

+

+    if (m_pName) {

+        (void)StringCchCopyW(pInfo->achName, NUMELMS(pInfo->achName), m_pName);

+    } else {

+        pInfo->achName[0] = L'\0';

+    }

+    pInfo->pGraph = m_pGraph;

+    if (m_pGraph)

+        m_pGraph->AddRef();

+    return NOERROR;

+}

+

+

+/* Provide the filter with a filter graph */

+

+STDMETHODIMP

+CBaseFilter::JoinFilterGraph(

+    __inout_opt IFilterGraph * pGraph,

+    __in_opt LPCWSTR pName)

+{

+    CAutoLock cObjectLock(m_pLock);

+

+    // NOTE: we no longer hold references on the graph (m_pGraph, m_pSink)

+

+    m_pGraph = pGraph;

+    if (m_pGraph) {

+        HRESULT hr = m_pGraph->QueryInterface(IID_IMediaEventSink,

+                        (void**) &m_pSink);

+        if (FAILED(hr)) {

+            ASSERT(m_pSink == NULL);

+        }

+        else m_pSink->Release();        // we do NOT keep a reference on it.

+    } else {

+        // if graph pointer is null, then we should

+        // also release the IMediaEventSink on the same object - we don't

+        // refcount it, so just set it to null

+        m_pSink = NULL;

+    }

+

+

+    if (m_pName) {

+        delete[] m_pName;

+        m_pName = NULL;

+    }

+

+    if (pName) {

+        size_t namelen;

+        HRESULT hr = StringCchLengthW(pName, STRSAFE_MAX_CCH, &namelen);

+        if (FAILED(hr)) {

+            return hr;

+        }

+        m_pName = new WCHAR[namelen + 1];

+        if (m_pName) {

+            (void)StringCchCopyW(m_pName, namelen + 1, pName);

+        } else {

+            return E_OUTOFMEMORY;

+        }

+    }

+

+#ifdef DXMPERF

+    PERFLOG_JOINGRAPH( m_pName ? m_pName : L"CBaseFilter",(IBaseFilter *) this, pGraph );

+#endif // DXMPERF

+

+    return NOERROR;

+}

+

+

+// return a Vendor information string. Optional - may return E_NOTIMPL.

+// memory returned should be freed using CoTaskMemFree

+// default implementation returns E_NOTIMPL

+STDMETHODIMP

+CBaseFilter::QueryVendorInfo(

+    __deref_out LPWSTR* pVendorInfo)

+{

+    UNREFERENCED_PARAMETER(pVendorInfo);

+    return E_NOTIMPL;

+}

+

+

+// send an event notification to the filter graph if we know about it.

+// returns S_OK if delivered, S_FALSE if the filter graph does not sink

+// events, or an error otherwise.

+HRESULT

+CBaseFilter::NotifyEvent(

+    long EventCode,

+    LONG_PTR EventParam1,

+    LONG_PTR EventParam2)

+{

+    // Snapshot so we don't have to lock up

+    IMediaEventSink *pSink = m_pSink;

+    if (pSink) {

+        if (EC_COMPLETE == EventCode) {

+            EventParam2 = (LONG_PTR)(IBaseFilter*)this;

+        }

+

+        return pSink->Notify(EventCode, EventParam1, EventParam2);

+    } else {

+        return E_NOTIMPL;

+    }

+}

+

+// Request reconnect

+// pPin is the pin to reconnect

+// pmt is the type to reconnect with - can be NULL

+// Calls ReconnectEx on the filter graph

+HRESULT

+CBaseFilter::ReconnectPin(

+    IPin *pPin,

+    __in_opt AM_MEDIA_TYPE const *pmt

+)

+{

+    IFilterGraph2 *pGraph2;

+    if (m_pGraph != NULL) {

+        HRESULT hr = m_pGraph->QueryInterface(IID_IFilterGraph2, (void **)&pGraph2);

+        if (SUCCEEDED(hr)) {

+            hr = pGraph2->ReconnectEx(pPin, pmt);

+            pGraph2->Release();

+            return hr;

+        } else {

+            return m_pGraph->Reconnect(pPin);

+        }

+    } else {

+        return E_NOINTERFACE;

+    }

+}

+

+

+

+/* This is the same idea as the media type version does for type enumeration

+   on pins but for the list of pins available. So if the list of pins you

+   provide changes dynamically then either override this virtual function

+   to provide the version number, or more simply call IncrementPinVersion */

+

+LONG CBaseFilter::GetPinVersion()

+{

+    return m_PinVersion;

+}

+

+

+/* Increment the current pin version cookie */

+

+void CBaseFilter::IncrementPinVersion()

+{

+    InterlockedIncrement(&m_PinVersion);

+}

+

+/* register filter */

+

+STDMETHODIMP CBaseFilter::Register()

+{

+    // get setup data, if it exists

+    //

+    LPAMOVIESETUP_FILTER psetupdata = GetSetupData();

+

+    // check we've got data

+    //

+    if( NULL == psetupdata ) return S_FALSE;

+

+    // init is ref counted so call just in case

+    // we're being called cold.

+    //

+    HRESULT hr = CoInitialize( (LPVOID)NULL );

+    ASSERT( SUCCEEDED(hr) );

+

+    // get hold of IFilterMapper

+    //

+    IFilterMapper *pIFM;

+    hr = CoCreateInstance( CLSID_FilterMapper

+                             , NULL

+                             , CLSCTX_INPROC_SERVER

+                             , IID_IFilterMapper

+                             , (void **)&pIFM       );

+    if( SUCCEEDED(hr) )

+    {

+        hr = AMovieSetupRegisterFilter( psetupdata, pIFM, TRUE );

+        pIFM->Release();

+    }

+

+    // and clear up

+    //

+    CoFreeUnusedLibraries();

+    CoUninitialize();

+

+    return NOERROR;

+}

+

+

+/* unregister filter */

+

+STDMETHODIMP CBaseFilter::Unregister()

+{

+    // get setup data, if it exists

+    //

+    LPAMOVIESETUP_FILTER psetupdata = GetSetupData();

+

+    // check we've got data

+    //

+    if( NULL == psetupdata ) return S_FALSE;

+

+    // OLE init is ref counted so call

+    // just in case we're being called cold.

+    //

+    HRESULT hr = CoInitialize( (LPVOID)NULL );

+    ASSERT( SUCCEEDED(hr) );

+

+    // get hold of IFilterMapper

+    //

+    IFilterMapper *pIFM;

+    hr = CoCreateInstance( CLSID_FilterMapper

+                             , NULL

+                             , CLSCTX_INPROC_SERVER

+                             , IID_IFilterMapper

+                             , (void **)&pIFM       );

+    if( SUCCEEDED(hr) )

+    {

+        hr = AMovieSetupRegisterFilter( psetupdata, pIFM, FALSE );

+

+        // release interface

+        //

+        pIFM->Release();

+    }

+

+    // clear up

+    //

+    CoFreeUnusedLibraries();

+    CoUninitialize();

+

+    // handle one acceptable "error" - that

+    // of filter not being registered!

+    // (couldn't find a suitable #define'd

+    // name for the error!)

+    //

+    if( 0x80070002 == hr)

+      return NOERROR;

+    else

+      return hr;

+}

+

+

+//=====================================================================

+//=====================================================================

+// Implements CEnumPins

+//=====================================================================

+//=====================================================================

+

+

+CEnumPins::CEnumPins(__in CBaseFilter *pFilter,

+                     __in_opt CEnumPins *pEnumPins) :

+    m_Position(0),

+    m_PinCount(0),

+    m_pFilter(pFilter),

+    m_cRef(1),               // Already ref counted

+    m_PinCache(NAME("Pin Cache"))

+{

+

+#ifdef DEBUG

+    m_dwCookie = DbgRegisterObjectCreation("CEnumPins", 0);

+#endif

+

+    /* We must be owned by a filter derived from CBaseFilter */

+

+    ASSERT(pFilter != NULL);

+

+    /* Hold a reference count on our filter */

+    m_pFilter->AddRef();

+

+    /* Are we creating a new enumerator */

+

+    if (pEnumPins == NULL) {

+        m_Version = m_pFilter->GetPinVersion();

+        m_PinCount = m_pFilter->GetPinCount();

+    } else {

+        ASSERT(m_Position <= m_PinCount);

+        m_Position = pEnumPins->m_Position;

+        m_PinCount = pEnumPins->m_PinCount;

+        m_Version = pEnumPins->m_Version;

+        m_PinCache.AddTail(&(pEnumPins->m_PinCache));

+    }

+}

+

+

+/* Destructor releases the reference count on our filter NOTE since we hold

+   a reference count on the filter who created us we know it is safe to

+   release it, no access can be made to it afterwards though as we have just

+   caused the last reference count to go and the object to be deleted */

+

+CEnumPins::~CEnumPins()

+{

+    m_pFilter->Release();

+

+#ifdef DEBUG

+    DbgRegisterObjectDestruction(m_dwCookie);

+#endif

+}

+

+

+/* Override this to say what interfaces we support where */

+

+STDMETHODIMP

+CEnumPins::QueryInterface(REFIID riid, __deref_out void **ppv)

+{

+    CheckPointer(ppv, E_POINTER);

+

+    /* Do we have this interface */

+

+    if (riid == IID_IEnumPins || riid == IID_IUnknown) {

+        return GetInterface((IEnumPins *) this, ppv);

+    } else {

+        *ppv = NULL;

+        return E_NOINTERFACE;

+    }

+}

+

+STDMETHODIMP_(ULONG)

+CEnumPins::AddRef()

+{

+    return InterlockedIncrement(&m_cRef);

+}

+

+STDMETHODIMP_(ULONG)

+CEnumPins::Release()

+{

+    ULONG cRef = InterlockedDecrement(&m_cRef);

+    if (cRef == 0) {

+        delete this;

+    }

+    return cRef;

+}

+

+/* One of an enumerator's basic member functions allows us to create a cloned

+   interface that initially has the same state. Since we are taking a snapshot

+   of an object (current position and all) we must lock access at the start */

+

+STDMETHODIMP 

+CEnumPins::Clone(__deref_out IEnumPins **ppEnum)

+{

+    CheckPointer(ppEnum,E_POINTER);

+    ValidateReadWritePtr(ppEnum,sizeof(IEnumPins *));

+    HRESULT hr = NOERROR;

+

+    /* Check we are still in sync with the filter */

+    if (AreWeOutOfSync() == TRUE) {

+        *ppEnum = NULL;

+        hr =  VFW_E_ENUM_OUT_OF_SYNC;

+    } else {

+        *ppEnum = new CEnumPins(m_pFilter, 

+                                this);

+        if (*ppEnum == NULL) {

+            hr = E_OUTOFMEMORY;

+        }

+    }

+    return hr;

+}

+

+

+/* Return the next pin after the current position */

+

+STDMETHODIMP

+CEnumPins::Next(ULONG cPins,        // place this many pins...

+        __out_ecount(cPins) IPin **ppPins,      // ...in this array

+        __out_opt ULONG *pcFetched)   // actual count passed returned here

+{

+    CheckPointer(ppPins,E_POINTER);

+    ValidateReadWritePtr(ppPins,cPins * sizeof(IPin *));

+

+    ASSERT(ppPins);

+

+    if (pcFetched!=NULL) {

+        ValidateWritePtr(pcFetched, sizeof(ULONG));

+        *pcFetched = 0;           // default unless we succeed

+    }

+    // now check that the parameter is valid

+    else if (cPins>1) {   // pcFetched == NULL

+        return E_INVALIDARG;

+    }

+    ULONG cFetched = 0;           // increment as we get each one.

+

+    /* Check we are still in sync with the filter */

+    if (AreWeOutOfSync() == TRUE) {

+        // If we are out of sync, we should refresh the enumerator.

+        // This will reset the position and update the other members, but

+        // will not clear cache of pins we have already returned.

+        Refresh();

+    }

+

+    /* Return each pin interface NOTE GetPin returns CBasePin * not addrefed

+       so we must QI for the IPin (which increments its reference count)

+       If while we are retrieving a pin from the filter an error occurs we

+       assume that our internal state is stale with respect to the filter

+       (for example someone has deleted a pin) so we

+       return VFW_E_ENUM_OUT_OF_SYNC                            */

+

+    while (cFetched < cPins && m_PinCount > m_Position) {

+

+        /* Get the next pin object from the filter */

+

+        CBasePin *pPin = m_pFilter->GetPin(m_Position++);

+        if (pPin == NULL) {

+            // If this happend, and it's not the first time through, then we've got a problem,

+            // since we should really go back and release the iPins, which we have previously

+            // AddRef'ed.

+            ASSERT( cFetched==0 );

+            return VFW_E_ENUM_OUT_OF_SYNC;

+        }

+

+        /* We only want to return this pin, if it is not in our cache */

+        if (0 == m_PinCache.Find(pPin))

+        {

+            /* From the object get an IPin interface */

+

+            *ppPins = pPin;

+            pPin->AddRef();

+

+            cFetched++;

+            ppPins++;

+

+            m_PinCache.AddTail(pPin);

+        }

+    }

+

+    if (pcFetched!=NULL) {

+        *pcFetched = cFetched;

+    }

+

+    return (cPins==cFetched ? NOERROR : S_FALSE);

+}

+

+

+/* Skip over one or more entries in the enumerator */

+

+STDMETHODIMP

+CEnumPins::Skip(ULONG cPins)

+{

+    /* Check we are still in sync with the filter */

+    if (AreWeOutOfSync() == TRUE) {

+        return VFW_E_ENUM_OUT_OF_SYNC;

+    }

+

+    /* Work out how many pins are left to skip over */

+    /* We could position at the end if we are asked to skip too many... */

+    /* ..which would match the base implementation for CEnumMediaTypes::Skip */

+

+    ULONG PinsLeft = m_PinCount - m_Position;

+    if (cPins > PinsLeft) {

+        return S_FALSE;

+    }

+    m_Position += cPins;

+    return NOERROR;

+}

+

+

+/* Set the current position back to the start */

+/* Reset has 4 simple steps:

+ *

+ * Set position to head of list

+ * Sync enumerator with object being enumerated

+ * Clear the cache of pins already returned

+ * return S_OK

+ */

+

+STDMETHODIMP

+CEnumPins::Reset()

+{

+    m_Version = m_pFilter->GetPinVersion();

+    m_PinCount = m_pFilter->GetPinCount();

+

+    m_Position = 0;

+

+    // Clear the cache

+    m_PinCache.RemoveAll();

+

+    return S_OK;

+}

+

+

+/* Set the current position back to the start */

+/* Refresh has 3 simple steps:

+ *

+ * Set position to head of list

+ * Sync enumerator with object being enumerated

+ * return S_OK

+ */

+

+STDMETHODIMP

+CEnumPins::Refresh()

+{

+    m_Version = m_pFilter->GetPinVersion();

+    m_PinCount = m_pFilter->GetPinCount();

+

+    m_Position = 0;

+    return S_OK;

+}

+

+

+//=====================================================================

+//=====================================================================

+// Implements CEnumMediaTypes

+//=====================================================================

+//=====================================================================

+

+

+CEnumMediaTypes::CEnumMediaTypes(__in CBasePin *pPin,

+                                 __in_opt CEnumMediaTypes *pEnumMediaTypes) :

+    m_Position(0),

+    m_pPin(pPin),

+    m_cRef(1)

+{

+

+#ifdef DEBUG

+    m_dwCookie = DbgRegisterObjectCreation("CEnumMediaTypes", 0);

+#endif

+

+    /* We must be owned by a pin derived from CBasePin */

+

+    ASSERT(pPin != NULL);

+

+    /* Hold a reference count on our pin */

+    m_pPin->AddRef();

+

+    /* Are we creating a new enumerator */

+

+    if (pEnumMediaTypes == NULL) {

+        m_Version = m_pPin->GetMediaTypeVersion();

+        return;

+    }

+

+    m_Position = pEnumMediaTypes->m_Position;

+    m_Version = pEnumMediaTypes->m_Version;

+}

+

+

+/* Destructor releases the reference count on our base pin. NOTE since we hold

+   a reference count on the pin who created us we know it is safe to release

+   it, no access can be made to it afterwards though as we might have just

+   caused the last reference count to go and the object to be deleted */

+

+CEnumMediaTypes::~CEnumMediaTypes()

+{

+#ifdef DEBUG

+    DbgRegisterObjectDestruction(m_dwCookie);

+#endif

+    m_pPin->Release();

+}

+

+

+/* Override this to say what interfaces we support where */

+

+STDMETHODIMP

+CEnumMediaTypes::QueryInterface(REFIID riid, __deref_out void **ppv)

+{

+    CheckPointer(ppv, E_POINTER);

+

+    /* Do we have this interface */

+

+    if (riid == IID_IEnumMediaTypes || riid == IID_IUnknown) {

+        return GetInterface((IEnumMediaTypes *) this, ppv);

+    } else {

+        *ppv = NULL;

+        return E_NOINTERFACE;

+    }

+}

+

+STDMETHODIMP_(ULONG)

+CEnumMediaTypes::AddRef()

+{

+    return InterlockedIncrement(&m_cRef);

+}

+

+STDMETHODIMP_(ULONG)

+CEnumMediaTypes::Release()

+{

+    ULONG cRef = InterlockedDecrement(&m_cRef);

+    if (cRef == 0) {

+        delete this;

+    }

+    return cRef;

+}

+

+/* One of an enumerator's basic member functions allows us to create a cloned

+   interface that initially has the same state. Since we are taking a snapshot

+   of an object (current position and all) we must lock access at the start */

+

+STDMETHODIMP

+CEnumMediaTypes::Clone(__deref_out IEnumMediaTypes **ppEnum)

+{

+    CheckPointer(ppEnum,E_POINTER);

+    ValidateReadWritePtr(ppEnum,sizeof(IEnumMediaTypes *));

+    HRESULT hr = NOERROR;

+

+    /* Check we are still in sync with the pin */

+    if (AreWeOutOfSync() == TRUE) {

+        *ppEnum = NULL;

+        hr = VFW_E_ENUM_OUT_OF_SYNC;

+    } else {

+

+        *ppEnum = new CEnumMediaTypes(m_pPin,

+                                      this);

+

+        if (*ppEnum == NULL) {

+            hr =  E_OUTOFMEMORY;

+        }

+    }

+    return hr;

+}

+

+

+/* Enumerate the next pin(s) after the current position. The client using this

+   interface passes in a pointer to an array of pointers each of which will

+   be filled in with a pointer to a fully initialised media type format

+   Return NOERROR if it all works,

+          S_FALSE if fewer than cMediaTypes were enumerated.

+          VFW_E_ENUM_OUT_OF_SYNC if the enumerator has been broken by

+                                 state changes in the filter

+   The actual count always correctly reflects the number of types in the array.

+*/

+

+STDMETHODIMP

+CEnumMediaTypes::Next(ULONG cMediaTypes,          // place this many types...

+                      __out_ecount(cMediaTypes) AM_MEDIA_TYPE **ppMediaTypes,   // ...in this array

+                      __out ULONG *pcFetched)           // actual count passed

+{

+    CheckPointer(ppMediaTypes,E_POINTER);

+    ValidateReadWritePtr(ppMediaTypes,cMediaTypes * sizeof(AM_MEDIA_TYPE *));

+    /* Check we are still in sync with the pin */

+    if (AreWeOutOfSync() == TRUE) {

+        return VFW_E_ENUM_OUT_OF_SYNC;

+    }

+

+    if (pcFetched!=NULL) {

+        ValidateWritePtr(pcFetched, sizeof(ULONG));

+        *pcFetched = 0;           // default unless we succeed

+    }

+    // now check that the parameter is valid

+    else if (cMediaTypes>1) {     // pcFetched == NULL

+        return E_INVALIDARG;

+    }

+    ULONG cFetched = 0;           // increment as we get each one.

+

+    /* Return each media type by asking the filter for them in turn - If we

+       have an error code retured to us while we are retrieving a media type

+       we assume that our internal state is stale with respect to the filter

+       (for example the window size changing) so we return

+       VFW_E_ENUM_OUT_OF_SYNC */

+

+    while (cMediaTypes) {

+

+        CMediaType cmt;

+

+        HRESULT hr = m_pPin->GetMediaType(m_Position++, &cmt);

+        if (S_OK != hr) {

+            break;

+        }

+

+        /* We now have a CMediaType object that contains the next media type

+           but when we assign it to the array position we CANNOT just assign

+           the AM_MEDIA_TYPE structure because as soon as the object goes out of

+           scope it will delete the memory we have just copied. The function

+           we use is CreateMediaType which allocates a task memory block */

+

+        /*  Transfer across the format block manually to save an allocate

+            and free on the format block and generally go faster */

+

+        *ppMediaTypes = (AM_MEDIA_TYPE *)CoTaskMemAlloc(sizeof(AM_MEDIA_TYPE));

+        if (*ppMediaTypes == NULL) {

+            break;

+        }

+

+        /*  Do a regular copy */

+        **ppMediaTypes = cmt;

+

+        /*  Make sure the destructor doesn't free these */

+        cmt.pbFormat = NULL;

+        cmt.cbFormat = NULL;

+        cmt.pUnk     = NULL;

+

+

+        ppMediaTypes++;

+        cFetched++;

+        cMediaTypes--;

+    }

+

+    if (pcFetched!=NULL) {

+        *pcFetched = cFetched;

+    }

+

+    return ( cMediaTypes==0 ? NOERROR : S_FALSE );

+}

+

+

+/* Skip over one or more entries in the enumerator */

+

+STDMETHODIMP

+CEnumMediaTypes::Skip(ULONG cMediaTypes)

+{

+    //  If we're skipping 0 elements we're guaranteed to skip the

+    //  correct number of elements

+    if (cMediaTypes == 0) {

+        return S_OK;

+    }

+

+    /* Check we are still in sync with the pin */

+    if (AreWeOutOfSync() == TRUE) {

+        return VFW_E_ENUM_OUT_OF_SYNC;

+    }

+

+    m_Position += cMediaTypes;

+

+    /*  See if we're over the end */

+    CMediaType cmt;

+    return S_OK == m_pPin->GetMediaType(m_Position - 1, &cmt) ? S_OK : S_FALSE;

+}

+

+

+/* Set the current position back to the start */

+/* Reset has 3 simple steps:

+ *

+ * set position to head of list

+ * sync enumerator with object being enumerated

+ * return S_OK

+ */

+

+STDMETHODIMP

+CEnumMediaTypes::Reset()

+

+{

+    m_Position = 0;

+

+    // Bring the enumerator back into step with the current state.  This

+    // may be a noop but ensures that the enumerator will be valid on the

+    // next call.

+    m_Version = m_pPin->GetMediaTypeVersion();

+    return NOERROR;

+}

+

+

+//=====================================================================

+//=====================================================================

+// Implements CBasePin

+//=====================================================================

+//=====================================================================

+

+

+/* NOTE The implementation of this class calls the CUnknown constructor with

+   a NULL outer unknown pointer. This has the effect of making us a self

+   contained class, ie any QueryInterface, AddRef or Release calls will be

+   routed to the class's NonDelegatingUnknown methods. You will typically

+   find that the classes that do this then override one or more of these

+   virtual functions to provide more specialised behaviour. A good example

+   of this is where a class wants to keep the QueryInterface internal but

+   still wants its lifetime controlled by the external object */

+

+/* Constructor */

+

+CBasePin::CBasePin(__in_opt LPCTSTR pObjectName,

+           __in CBaseFilter *pFilter,

+           __in CCritSec *pLock,

+           __inout HRESULT *phr,

+           __in_opt LPCWSTR pName,

+           PIN_DIRECTION dir) :

+    CUnknown( pObjectName, NULL ),

+    m_pFilter(pFilter),

+    m_pLock(pLock),

+    m_pName(NULL),

+    m_Connected(NULL),

+    m_dir(dir),

+    m_bRunTimeError(FALSE),

+    m_pQSink(NULL),

+    m_TypeVersion(1),

+    m_tStart(),

+    m_tStop(MAX_TIME),

+    m_bCanReconnectWhenActive(false),

+    m_bTryMyTypesFirst(false),

+    m_dRate(1.0)

+{

+    /*  WARNING - pFilter is often not a properly constituted object at

+        this state (in particular QueryInterface may not work) - this

+        is because its owner is often its containing object and we

+        have been called from the containing object's constructor so

+        the filter's owner has not yet had its CUnknown constructor

+        called

+    */

+#ifdef DXMPERF

+    PERFLOG_CTOR( pName ? pName : L"CBasePin", (IPin *) this );

+#endif // DXMPERF

+

+    ASSERT(pFilter != NULL);

+    ASSERT(pLock != NULL);

+

+    if (pName) {

+        size_t cchName;

+        HRESULT hr = StringCchLengthW(pName, STRSAFE_MAX_CCH, &cchName);

+        if (SUCCEEDED(hr)) {

+            m_pName = new WCHAR[cchName + 1];

+            if (m_pName) {

+                (void)StringCchCopyW(m_pName, cchName + 1, pName);

+            }

+        }

+    }

+

+#ifdef DEBUG

+    m_cRef = 0;

+#endif

+}

+

+#ifdef UNICODE

+CBasePin::CBasePin(__in_opt LPCSTR pObjectName,

+           __in CBaseFilter *pFilter,

+           __in CCritSec *pLock,

+           __inout HRESULT *phr,

+           __in_opt LPCWSTR pName,

+           PIN_DIRECTION dir) :

+    CUnknown( pObjectName, NULL ),

+    m_pFilter(pFilter),

+    m_pLock(pLock),

+    m_pName(NULL),

+    m_Connected(NULL),

+    m_dir(dir),

+    m_bRunTimeError(FALSE),

+    m_pQSink(NULL),

+    m_TypeVersion(1),

+    m_tStart(),

+    m_tStop(MAX_TIME),

+    m_bCanReconnectWhenActive(false),

+    m_bTryMyTypesFirst(false),

+    m_dRate(1.0)

+{

+    /*  WARNING - pFilter is often not a properly constituted object at

+        this state (in particular QueryInterface may not work) - this

+        is because its owner is often its containing object and we

+        have been called from the containing object's constructor so

+        the filter's owner has not yet had its CUnknown constructor

+        called

+    */

+#ifdef DXMPERF

+    PERFLOG_CTOR( pName ? pName : L"CBasePin", (IPin *) this );

+#endif // DXMPERF

+

+    ASSERT(pFilter != NULL);

+    ASSERT(pLock != NULL);

+

+    if (pName) {

+        size_t cchName;

+        HRESULT hr = StringCchLengthW(pName, STRSAFE_MAX_CCH, &cchName);

+        if (SUCCEEDED(hr)) {

+            m_pName = new WCHAR[cchName + 1];

+            if (m_pName) {

+                (void)StringCchCopyW(m_pName, cchName + 1, pName);

+            }

+        }

+    }

+

+

+#ifdef DEBUG

+    m_cRef = 0;

+#endif

+}

+#endif

+

+/* Destructor since a connected pin holds a reference count on us there is

+   no way that we can be deleted unless we are not currently connected */

+

+CBasePin::~CBasePin()

+{

+#ifdef DXMPERF

+    PERFLOG_DTOR( m_pName ? m_pName : L"CBasePin", (IPin *) this );

+#endif // DXMPERF

+

+    //  We don't call disconnect because if the filter is going away

+    //  all the pins must have a reference count of zero so they must

+    //  have been disconnected anyway - (but check the assumption)

+    ASSERT(m_Connected == FALSE);

+

+    delete[] m_pName;

+

+    // check the internal reference count is consistent

+    ASSERT(m_cRef == 0);

+}

+

+

+/* Override this to say what interfaces we support and where */

+

+STDMETHODIMP

+CBasePin::NonDelegatingQueryInterface(REFIID riid, __deref_out void ** ppv)

+{

+    /* Do we have this interface */

+

+    if (riid == IID_IPin) {

+        return GetInterface((IPin *) this, ppv);

+    } else if (riid == IID_IQualityControl) {

+        return GetInterface((IQualityControl *) this, ppv);

+    } else {

+        return CUnknown::NonDelegatingQueryInterface(riid, ppv);

+    }

+}

+

+

+/* Override to increment the owning filter's reference count */

+

+STDMETHODIMP_(ULONG)

+CBasePin::NonDelegatingAddRef()

+{

+    ASSERT(InterlockedIncrement(&m_cRef) > 0);

+    return m_pFilter->AddRef();

+}

+

+

+/* Override to decrement the owning filter's reference count */

+

+STDMETHODIMP_(ULONG)

+CBasePin::NonDelegatingRelease()

+{

+    ASSERT(InterlockedDecrement(&m_cRef) >= 0);

+    return m_pFilter->Release();

+}

+

+

+/* Displays pin connection information */

+

+#ifdef DEBUG

+void

+CBasePin::DisplayPinInfo(IPin *pReceivePin)

+{

+

+    if (DbgCheckModuleLevel(LOG_TRACE, CONNECT_TRACE_LEVEL)) {

+        PIN_INFO ConnectPinInfo;

+        PIN_INFO ReceivePinInfo;

+

+        if (FAILED(QueryPinInfo(&ConnectPinInfo))) {

+            StringCchCopyW(ConnectPinInfo.achName, sizeof(ConnectPinInfo.achName)/sizeof(WCHAR), L"Bad Pin");

+        } else {

+            QueryPinInfoReleaseFilter(ConnectPinInfo);

+        }

+

+        if (FAILED(pReceivePin->QueryPinInfo(&ReceivePinInfo))) {

+            StringCchCopyW(ReceivePinInfo.achName, sizeof(ReceivePinInfo.achName)/sizeof(WCHAR), L"Bad Pin");

+        } else {

+            QueryPinInfoReleaseFilter(ReceivePinInfo);

+        }

+

+        DbgLog((LOG_TRACE, CONNECT_TRACE_LEVEL, TEXT("Trying to connect Pins :")));

+        DbgLog((LOG_TRACE, CONNECT_TRACE_LEVEL, TEXT("    <%ls>"), ConnectPinInfo.achName));

+        DbgLog((LOG_TRACE, CONNECT_TRACE_LEVEL, TEXT("    <%ls>"), ReceivePinInfo.achName));

+    }

+}

+#endif

+

+

+/* Displays general information on the pin media type */

+

+#ifdef DEBUG

+void CBasePin::DisplayTypeInfo(IPin *pPin, const CMediaType *pmt)

+{

+    UNREFERENCED_PARAMETER(pPin);

+    if (DbgCheckModuleLevel(LOG_TRACE, CONNECT_TRACE_LEVEL)) {

+        DbgLog((LOG_TRACE, CONNECT_TRACE_LEVEL, TEXT("Trying media type:")));

+        DbgLog((LOG_TRACE, CONNECT_TRACE_LEVEL, TEXT("    major type:  %hs"),

+               GuidNames[*pmt->Type()]));

+        DbgLog((LOG_TRACE, CONNECT_TRACE_LEVEL, TEXT("    sub type  :  %hs"),

+               GuidNames[*pmt->Subtype()]));

+    }

+}

+#endif

+

+/* Asked to connect to a pin. A pin is always attached to an owning filter

+   object so we always delegate our locking to that object. We first of all

+   retrieve a media type enumerator for the input pin and see if we accept

+   any of the formats that it would ideally like, failing that we retrieve

+   our enumerator and see if it will accept any of our preferred types */

+

+STDMETHODIMP

+CBasePin::Connect(

+    IPin * pReceivePin,

+    __in_opt const AM_MEDIA_TYPE *pmt   // optional media type

+)

+{

+    CheckPointer(pReceivePin,E_POINTER);

+    ValidateReadPtr(pReceivePin,sizeof(IPin));

+    CAutoLock cObjectLock(m_pLock);

+    DisplayPinInfo(pReceivePin);

+

+    /* See if we are already connected */

+

+    if (m_Connected) {

+        DbgLog((LOG_TRACE, CONNECT_TRACE_LEVEL, TEXT("Already connected")));

+        return VFW_E_ALREADY_CONNECTED;

+    }

+

+    /* See if the filter is active */

+    if (!IsStopped() && !m_bCanReconnectWhenActive) {

+        return VFW_E_NOT_STOPPED;

+    }

+

+

+    // Find a mutually agreeable media type -

+    // Pass in the template media type. If this is partially specified,

+    // each of the enumerated media types will need to be checked against

+    // it. If it is non-null and fully specified, we will just try to connect

+    // with this.

+

+    const CMediaType * ptype = (CMediaType*)pmt;

+    HRESULT hr = AgreeMediaType(pReceivePin, ptype);

+    if (FAILED(hr)) {

+        DbgLog((LOG_TRACE, CONNECT_TRACE_LEVEL, TEXT("Failed to agree type")));

+

+        // Since the procedure is already returning an error code, there

+        // is nothing else this function can do to report the error.

+        EXECUTE_ASSERT( SUCCEEDED( BreakConnect() ) );

+

+#ifdef DXMPERF

+        PERFLOG_CONNECT( (IPin *) this, pReceivePin, hr, pmt );

+#endif // DXMPERF

+

+        return hr;

+    }

+

+    DbgLog((LOG_TRACE, CONNECT_TRACE_LEVEL, TEXT("Connection succeeded")));

+

+#ifdef DXMPERF

+    PERFLOG_CONNECT( (IPin *) this, pReceivePin, NOERROR, pmt );

+#endif // DXMPERF

+

+    return NOERROR;

+}

+

+// given a specific media type, attempt a connection (includes

+// checking that the type is acceptable to this pin)

+HRESULT

+CBasePin::AttemptConnection(

+    IPin* pReceivePin,      // connect to this pin

+    const CMediaType* pmt   // using this type

+)

+{

+    // The caller should hold the filter lock becasue this function

+    // uses m_Connected.  The caller should also hold the filter lock

+    // because this function calls SetMediaType(), IsStopped() and

+    // CompleteConnect().

+    ASSERT(CritCheckIn(m_pLock));

+

+    // Check that the connection is valid  -- need to do this for every

+    // connect attempt since BreakConnect will undo it.

+    HRESULT hr = CheckConnect(pReceivePin);

+    if (FAILED(hr)) {

+        DbgLog((LOG_TRACE, CONNECT_TRACE_LEVEL, TEXT("CheckConnect failed")));

+

+        // Since the procedure is already returning an error code, there

+        // is nothing else this function can do to report the error.

+        EXECUTE_ASSERT( SUCCEEDED( BreakConnect() ) );

+

+        return hr;

+    }

+

+    DisplayTypeInfo(pReceivePin, pmt);

+

+    /* Check we will accept this media type */

+

+    hr = CheckMediaType(pmt);

+    if (hr == NOERROR) {

+

+        /*  Make ourselves look connected otherwise ReceiveConnection

+            may not be able to complete the connection

+        */

+        m_Connected = pReceivePin;

+        m_Connected->AddRef();

+        hr = SetMediaType(pmt);

+        if (SUCCEEDED(hr)) {

+            /* See if the other pin will accept this type */

+

+            hr = pReceivePin->ReceiveConnection((IPin *)this, pmt);

+            if (SUCCEEDED(hr)) {

+                /* Complete the connection */

+

+                hr = CompleteConnect(pReceivePin);

+                if (SUCCEEDED(hr)) {

+                    return hr;

+                } else {

+                    DbgLog((LOG_TRACE,

+                            CONNECT_TRACE_LEVEL,

+                            TEXT("Failed to complete connection")));

+                    pReceivePin->Disconnect();

+                }

+            }

+        }

+    } else {

+        // we cannot use this media type

+

+        // return a specific media type error if there is one

+        // or map a general failure code to something more helpful

+        // (in particular S_FALSE gets changed to an error code)

+        if (SUCCEEDED(hr) ||

+            (hr == E_FAIL) ||

+            (hr == E_INVALIDARG)) {

+            hr = VFW_E_TYPE_NOT_ACCEPTED;

+        }

+    }

+

+    // BreakConnect and release any connection here in case CheckMediaType

+    // failed, or if we set anything up during a call back during

+    // ReceiveConnection.

+

+    // Since the procedure is already returning an error code, there

+    // is nothing else this function can do to report the error.

+    EXECUTE_ASSERT( SUCCEEDED( BreakConnect() ) );

+

+    /*  If failed then undo our state */

+    if (m_Connected) {

+        m_Connected->Release();

+        m_Connected = NULL;

+    }

+

+    return hr;

+}

+

+/* Given an enumerator we cycle through all the media types it proposes and

+   firstly suggest them to our derived pin class and if that succeeds try

+   them with the pin in a ReceiveConnection call. This means that if our pin

+   proposes a media type we still check in here that we can support it. This

+   is deliberate so that in simple cases the enumerator can hold all of the

+   media types even if some of them are not really currently available */

+

+HRESULT CBasePin::TryMediaTypes(

+    IPin *pReceivePin,

+    __in_opt const CMediaType *pmt,

+    IEnumMediaTypes *pEnum)

+{

+    /* Reset the current enumerator position */

+

+    HRESULT hr = pEnum->Reset();

+    if (FAILED(hr)) {

+        return hr;

+    }

+

+    CMediaType *pMediaType = NULL;

+    ULONG ulMediaCount = 0;

+

+    // attempt to remember a specific error code if there is one

+    HRESULT hrFailure = S_OK;

+

+    for (;;) {

+

+        /* Retrieve the next media type NOTE each time round the loop the

+           enumerator interface will allocate another AM_MEDIA_TYPE structure

+           If we are successful then we copy it into our output object, if

+           not then we must delete the memory allocated before returning */

+

+        hr = pEnum->Next(1, (AM_MEDIA_TYPE**)&pMediaType,&ulMediaCount);

+        if (hr != S_OK) {

+            if (S_OK == hrFailure) {

+                hrFailure = VFW_E_NO_ACCEPTABLE_TYPES;

+            }

+            return hrFailure;

+        }

+

+

+        ASSERT(ulMediaCount == 1);

+        ASSERT(pMediaType);

+

+        // check that this matches the partial type (if any)

+

+        if (pMediaType &&

+            ((pmt == NULL) ||

+            pMediaType->MatchesPartial(pmt))) {

+

+            hr = AttemptConnection(pReceivePin, pMediaType);

+

+            // attempt to remember a specific error code

+            if (FAILED(hr) &&

+            SUCCEEDED(hrFailure) &&

+            (hr != E_FAIL) &&

+            (hr != E_INVALIDARG) &&

+            (hr != VFW_E_TYPE_NOT_ACCEPTED)) {

+                hrFailure = hr;

+            }

+        } else {

+            hr = VFW_E_NO_ACCEPTABLE_TYPES;

+        }

+

+        if(pMediaType) {

+            DeleteMediaType(pMediaType);

+            pMediaType = NULL;

+        }

+

+        if (S_OK == hr) {

+            return hr;

+        }

+    }

+}

+

+

+/* This is called to make the connection, including the taask of finding

+   a media type for the pin connection. pmt is the proposed media type

+   from the Connect call: if this is fully specified, we will try that.

+   Otherwise we enumerate and try all the input pin's types first and

+   if that fails we then enumerate and try all our preferred media types.

+   For each media type we check it against pmt (if non-null and partially

+   specified) as well as checking that both pins will accept it.

+ */

+

+HRESULT CBasePin::AgreeMediaType(

+    IPin *pReceivePin,

+    const CMediaType *pmt)

+{

+    ASSERT(pReceivePin);

+    IEnumMediaTypes *pEnumMediaTypes = NULL;

+

+    // if the media type is fully specified then use that

+    if ( (pmt != NULL) && (!pmt->IsPartiallySpecified())) {

+

+        // if this media type fails, then we must fail the connection

+        // since if pmt is nonnull we are only allowed to connect

+        // using a type that matches it.

+

+        return AttemptConnection(pReceivePin, pmt);

+    }

+

+

+    /* Try the other pin's enumerator */

+

+    HRESULT hrFailure = VFW_E_NO_ACCEPTABLE_TYPES;

+

+    for (int i = 0; i < 2; i++) {

+        HRESULT hr;

+        if (i == (int)m_bTryMyTypesFirst) {

+            hr = pReceivePin->EnumMediaTypes(&pEnumMediaTypes);

+        } else {

+            hr = EnumMediaTypes(&pEnumMediaTypes);

+        }

+        if (SUCCEEDED(hr)) {

+            ASSERT(pEnumMediaTypes);

+            hr = TryMediaTypes(pReceivePin,pmt,pEnumMediaTypes);

+            pEnumMediaTypes->Release();

+            if (SUCCEEDED(hr)) {

+                return NOERROR;

+            } else {

+                // try to remember specific error codes if there are any

+                if ((hr != E_FAIL) &&

+                    (hr != E_INVALIDARG) &&

+                    (hr != VFW_E_TYPE_NOT_ACCEPTED)) {

+                    hrFailure = hr;

+                }

+            }

+        }

+    }

+

+    return hrFailure;

+}

+

+

+/* Called when we want to complete a connection to another filter. Failing

+   this will also fail the connection and disconnect the other pin as well */

+

+HRESULT

+CBasePin::CompleteConnect(IPin *pReceivePin)

+{

+    UNREFERENCED_PARAMETER(pReceivePin);

+    return NOERROR;

+}

+

+

+/* This is called to set the format for a pin connection - CheckMediaType

+   will have been called to check the connection format and if it didn't

+   return an error code then this (virtual) function will be invoked */

+

+HRESULT

+CBasePin::SetMediaType(const CMediaType *pmt)

+{

+    HRESULT hr = m_mt.Set(*pmt);

+    if (FAILED(hr)) {

+        return hr;

+    }

+

+    return NOERROR;

+}

+

+

+/* This is called during Connect() to provide a virtual method that can do

+   any specific check needed for connection such as QueryInterface. This

+   base class method just checks that the pin directions don't match */

+

+HRESULT

+CBasePin::CheckConnect(IPin * pPin)

+{

+    /* Check that pin directions DONT match */

+

+    PIN_DIRECTION pd;

+    pPin->QueryDirection(&pd);

+

+    ASSERT((pd == PINDIR_OUTPUT) || (pd == PINDIR_INPUT));

+    ASSERT((m_dir == PINDIR_OUTPUT) || (m_dir == PINDIR_INPUT));

+

+    // we should allow for non-input and non-output connections?

+    if (pd == m_dir) {

+        return VFW_E_INVALID_DIRECTION;

+    }

+    return NOERROR;

+}

+

+

+/* This is called when we realise we can't make a connection to the pin and

+   must undo anything we did in CheckConnect - override to release QIs done */

+

+HRESULT

+CBasePin::BreakConnect()

+{

+    return NOERROR;

+}

+

+

+/* Called normally by an output pin on an input pin to try and establish a

+   connection.

+*/

+

+STDMETHODIMP

+CBasePin::ReceiveConnection(

+    IPin * pConnector,   // this is the pin who we will connect to

+    const AM_MEDIA_TYPE *pmt  // this is the media type we will exchange

+)

+{

+    CheckPointer(pConnector,E_POINTER);

+    CheckPointer(pmt,E_POINTER);

+    ValidateReadPtr(pConnector,sizeof(IPin));

+    ValidateReadPtr(pmt,sizeof(AM_MEDIA_TYPE));

+    CAutoLock cObjectLock(m_pLock);

+

+    /* Are we already connected */

+    if (m_Connected) {

+        return VFW_E_ALREADY_CONNECTED;

+    }

+

+    /* See if the filter is active */

+    if (!IsStopped() && !m_bCanReconnectWhenActive) {

+        return VFW_E_NOT_STOPPED;

+    }

+

+    HRESULT hr = CheckConnect(pConnector);

+    if (FAILED(hr)) {

+        // Since the procedure is already returning an error code, there

+        // is nothing else this function can do to report the error.

+        EXECUTE_ASSERT( SUCCEEDED( BreakConnect() ) );

+

+#ifdef DXMPERF

+        PERFLOG_RXCONNECT( pConnector, (IPin *) this, hr, pmt );

+#endif // DXMPERF

+

+        return hr;

+    }

+

+    /* Ask derived class if this media type is ok */

+

+    CMediaType * pcmt = (CMediaType*) pmt;

+    hr = CheckMediaType(pcmt);

+    if (hr != NOERROR) {

+        // no -we don't support this media type

+

+        // Since the procedure is already returning an error code, there

+        // is nothing else this function can do to report the error.

+        EXECUTE_ASSERT( SUCCEEDED( BreakConnect() ) );

+

+        // return a specific media type error if there is one

+        // or map a general failure code to something more helpful

+        // (in particular S_FALSE gets changed to an error code)

+        if (SUCCEEDED(hr) ||

+            (hr == E_FAIL) ||

+            (hr == E_INVALIDARG)) {

+            hr = VFW_E_TYPE_NOT_ACCEPTED;

+        }

+

+#ifdef DXMPERF

+        PERFLOG_RXCONNECT( pConnector, (IPin *) this, hr, pmt );

+#endif // DXMPERF

+

+        return hr;

+    }

+

+    /* Complete the connection */

+

+    m_Connected = pConnector;

+    m_Connected->AddRef();

+    hr = SetMediaType(pcmt);

+    if (SUCCEEDED(hr)) {

+        hr = CompleteConnect(pConnector);

+        if (SUCCEEDED(hr)) {

+

+#ifdef DXMPERF

+            PERFLOG_RXCONNECT( pConnector, (IPin *) this, NOERROR, pmt );

+#endif // DXMPERF

+

+            return NOERROR;

+        }

+    }

+

+    DbgLog((LOG_TRACE, CONNECT_TRACE_LEVEL, TEXT("Failed to set the media type or failed to complete the connection.")));

+    m_Connected->Release();

+    m_Connected = NULL;

+

+    // Since the procedure is already returning an error code, there

+    // is nothing else this function can do to report the error.

+    EXECUTE_ASSERT( SUCCEEDED( BreakConnect() ) );

+

+#ifdef DXMPERF

+    PERFLOG_RXCONNECT( pConnector, (IPin *) this, hr, pmt );

+#endif // DXMPERF

+

+    return hr;

+}

+

+

+/* Called when we want to terminate a pin connection */

+

+STDMETHODIMP

+CBasePin::Disconnect()

+{

+    CAutoLock cObjectLock(m_pLock);

+

+    /* See if the filter is active */

+    if (!IsStopped()) {

+        return VFW_E_NOT_STOPPED;

+    }

+

+    return DisconnectInternal();

+}

+

+STDMETHODIMP

+CBasePin::DisconnectInternal()

+{

+    ASSERT(CritCheckIn(m_pLock));

+

+    if (m_Connected) {

+        HRESULT hr = BreakConnect();

+        if( FAILED( hr ) ) {

+

+#ifdef DXMPERF

+            PERFLOG_DISCONNECT( (IPin *) this, m_Connected, hr );

+#endif // DXMPERF

+

+            // There is usually a bug in the program if BreakConnect() fails.

+            DbgBreak( "WARNING: BreakConnect() failed in CBasePin::Disconnect()." );

+            return hr;

+        }

+

+        m_Connected->Release();

+        m_Connected = NULL;

+

+#ifdef DXMPERF

+        PERFLOG_DISCONNECT( (IPin *) this, m_Connected, S_OK );

+#endif // DXMPERF

+

+        return S_OK;

+    } else {

+        // no connection - not an error

+

+#ifdef DXMPERF

+        PERFLOG_DISCONNECT( (IPin *) this, m_Connected, S_FALSE );

+#endif // DXMPERF

+

+        return S_FALSE;

+    }

+}

+

+

+/* Return an AddRef()'d pointer to the connected pin if there is one */

+STDMETHODIMP

+CBasePin::ConnectedTo(

+    __deref_out IPin **ppPin

+)

+{

+    CheckPointer(ppPin,E_POINTER);

+    ValidateReadWritePtr(ppPin,sizeof(IPin *));

+    //

+    //  It's pointless to lock here.

+    //  The caller should ensure integrity.

+    //

+

+    IPin *pPin = m_Connected;

+    *ppPin = pPin;

+    if (pPin != NULL) {

+        pPin->AddRef();

+        return S_OK;

+    } else {

+        ASSERT(*ppPin == NULL);

+        return VFW_E_NOT_CONNECTED;

+    }

+}

+

+/* Return the media type of the connection */

+STDMETHODIMP

+CBasePin::ConnectionMediaType(

+    __out AM_MEDIA_TYPE *pmt

+)

+{

+    CheckPointer(pmt,E_POINTER);

+    ValidateReadWritePtr(pmt,sizeof(AM_MEDIA_TYPE));

+    CAutoLock cObjectLock(m_pLock);

+

+    /*  Copy constructor of m_mt allocates the memory */

+    if (IsConnected()) {

+        CopyMediaType( pmt, &m_mt );

+        return S_OK;

+    } else {

+        ((CMediaType *)pmt)->InitMediaType();

+        return VFW_E_NOT_CONNECTED;

+    }

+}

+

+/* Return information about the filter we are connect to */

+

+STDMETHODIMP

+CBasePin::QueryPinInfo(

+    __out PIN_INFO * pInfo

+)

+{

+    CheckPointer(pInfo,E_POINTER);

+    ValidateReadWritePtr(pInfo,sizeof(PIN_INFO));

+

+    pInfo->pFilter = m_pFilter;

+    if (m_pFilter) {

+        m_pFilter->AddRef();

+    }

+

+    if (m_pName) {

+        (void)StringCchCopyW(pInfo->achName, NUMELMS(pInfo->achName), m_pName);

+    } else {

+        pInfo->achName[0] = L'\0';

+    }

+

+    pInfo->dir = m_dir;

+

+    return NOERROR;

+}

+

+STDMETHODIMP

+CBasePin::QueryDirection(

+    __out PIN_DIRECTION * pPinDir

+)

+{

+    CheckPointer(pPinDir,E_POINTER);

+    ValidateReadWritePtr(pPinDir,sizeof(PIN_DIRECTION));

+

+    *pPinDir = m_dir;

+    return NOERROR;

+}

+

+// Default QueryId to return the pin's name

+STDMETHODIMP

+CBasePin::QueryId(

+    __deref_out LPWSTR * Id

+)

+{

+    //  We're not going away because someone's got a pointer to us

+    //  so there's no need to lock

+

+    return AMGetWideString(Name(), Id);

+}

+

+/* Does this pin support this media type WARNING this interface function does

+   not lock the main object as it is meant to be asynchronous by nature - if

+   the media types you support depend on some internal state that is updated

+   dynamically then you will need to implement locking in a derived class */

+

+STDMETHODIMP

+CBasePin::QueryAccept(

+    const AM_MEDIA_TYPE *pmt

+)

+{

+    CheckPointer(pmt,E_POINTER);

+    ValidateReadPtr(pmt,sizeof(AM_MEDIA_TYPE));

+

+    /* The CheckMediaType method is valid to return error codes if the media

+       type is horrible, an example might be E_INVALIDARG. What we do here

+       is map all the error codes into either S_OK or S_FALSE regardless */

+

+    HRESULT hr = CheckMediaType((CMediaType*)pmt);

+    if (FAILED(hr)) {

+        return S_FALSE;

+    }

+    // note that the only defined success codes should be S_OK and S_FALSE...

+    return hr;

+}

+

+

+/* This can be called to return an enumerator for the pin's list of preferred

+   media types. An input pin is not obliged to have any preferred formats

+   although it can do. For example, the window renderer has a preferred type

+   which describes a video image that matches the current window size. All

+   output pins should expose at least one preferred format otherwise it is

+   possible that neither pin has any types and so no connection is possible */

+

+STDMETHODIMP

+CBasePin::EnumMediaTypes(

+    __deref_out IEnumMediaTypes **ppEnum

+)

+{

+    CheckPointer(ppEnum,E_POINTER);

+    ValidateReadWritePtr(ppEnum,sizeof(IEnumMediaTypes *));

+

+    /* Create a new ref counted enumerator */

+

+    *ppEnum = new CEnumMediaTypes(this,

+                              NULL);

+

+    if (*ppEnum == NULL) {

+        return E_OUTOFMEMORY;

+    }

+

+    return NOERROR;

+}

+

+

+

+/* This is a virtual function that returns a media type corresponding with

+   place iPosition in the list. This base class simply returns an error as

+   we support no media types by default but derived classes should override */

+

+HRESULT CBasePin::GetMediaType(int iPosition, __inout CMediaType *pMediaType)

+{

+    UNREFERENCED_PARAMETER(iPosition);

+    UNREFERENCED_PARAMETER(pMediaType);

+    return E_UNEXPECTED;

+}

+

+

+/* This is a virtual function that returns the current media type version.

+   The base class initialises the media type enumerators with the value 1

+   By default we always returns that same value. A Derived class may change

+   the list of media types available and after doing so it should increment

+   the version either in a method derived from this, or more simply by just

+   incrementing the m_TypeVersion base pin variable. The type enumerators

+   call this when they want to see if their enumerations are out of date */

+

+LONG CBasePin::GetMediaTypeVersion()

+{

+    return m_TypeVersion;

+}

+

+

+/* Increment the cookie representing the current media type version */

+

+void CBasePin::IncrementTypeVersion()

+{

+    InterlockedIncrement(&m_TypeVersion);

+}

+

+

+/* Called by IMediaFilter implementation when the state changes from Stopped

+   to either paused or running and in derived classes could do things like

+   commit memory and grab hardware resource (the default is to do nothing) */

+

+HRESULT

+CBasePin::Active(void)

+{

+    return NOERROR;

+}

+

+/* Called by IMediaFilter implementation when the state changes from

+   to either paused to running and in derived classes could do things like

+   commit memory and grab hardware resource (the default is to do nothing) */

+

+HRESULT

+CBasePin::Run(REFERENCE_TIME tStart)

+{

+    UNREFERENCED_PARAMETER(tStart);

+    return NOERROR;

+}

+

+

+/* Also called by the IMediaFilter implementation when the state changes to

+   Stopped at which point you should decommit allocators and free hardware

+   resources you grabbed in the Active call (default is also to do nothing) */

+

+HRESULT

+CBasePin::Inactive(void)

+{

+    m_bRunTimeError = FALSE;

+    return NOERROR;

+}

+

+

+// Called when no more data will arrive

+STDMETHODIMP

+CBasePin::EndOfStream(void)

+{

+    return S_OK;

+}

+

+

+STDMETHODIMP

+CBasePin::SetSink(IQualityControl * piqc)

+{

+    CAutoLock cObjectLock(m_pLock);

+    if (piqc) ValidateReadPtr(piqc,sizeof(IQualityControl));

+    m_pQSink = piqc;

+    return NOERROR;

+} // SetSink

+

+

+STDMETHODIMP

+CBasePin::Notify(IBaseFilter * pSender, Quality q)

+{

+    UNREFERENCED_PARAMETER(q);

+    UNREFERENCED_PARAMETER(pSender);

+    DbgBreak("IQualityControl::Notify not over-ridden from CBasePin.  (IGNORE is OK)");

+    return E_NOTIMPL;

+} //Notify

+

+

+// NewSegment notifies of the start/stop/rate applying to the data

+// about to be received. Default implementation records data and

+// returns S_OK.

+// Override this to pass downstream.

+STDMETHODIMP

+CBasePin::NewSegment(

+                REFERENCE_TIME tStart,

+                REFERENCE_TIME tStop,

+                double dRate)

+{

+    m_tStart = tStart;

+    m_tStop = tStop;

+    m_dRate = dRate;

+

+    return S_OK;

+}

+

+

+//=====================================================================

+//=====================================================================

+// Implements CBaseOutputPin

+//=====================================================================

+//=====================================================================

+

+

+CBaseOutputPin::CBaseOutputPin(__in_opt LPCTSTR pObjectName,

+                   __in CBaseFilter *pFilter,

+                   __in CCritSec *pLock,

+                   __inout HRESULT *phr,

+                   __in_opt LPCWSTR pName) :

+    CBasePin(pObjectName, pFilter, pLock, phr, pName, PINDIR_OUTPUT),

+    m_pAllocator(NULL),

+    m_pInputPin(NULL)

+{

+    ASSERT(pFilter);

+}

+

+#ifdef UNICODE

+CBaseOutputPin::CBaseOutputPin(__in_opt LPCSTR pObjectName,

+                   __in CBaseFilter *pFilter,

+                   __in CCritSec *pLock,

+                   __inout HRESULT *phr,

+                   __in_opt LPCWSTR pName) :

+    CBasePin(pObjectName, pFilter, pLock, phr, pName, PINDIR_OUTPUT),

+    m_pAllocator(NULL),

+    m_pInputPin(NULL)

+{

+    ASSERT(pFilter);

+}

+#endif

+

+/*   This is called after a media type has been proposed

+

+     Try to complete the connection by agreeing the allocator

+*/

+HRESULT

+CBaseOutputPin::CompleteConnect(IPin *pReceivePin)

+{

+    UNREFERENCED_PARAMETER(pReceivePin);

+    return DecideAllocator(m_pInputPin, &m_pAllocator);

+}

+

+

+/* This method is called when the output pin is about to try and connect to

+   an input pin. It is at this point that you should try and grab any extra

+   interfaces that you need, in this case IMemInputPin. Because this is

+   only called if we are not currently connected we do NOT need to call

+   BreakConnect. This also makes it easier to derive classes from us as

+   BreakConnect is only called when we actually have to break a connection

+   (or a partly made connection) and not when we are checking a connection */

+

+/* Overriden from CBasePin */

+

+HRESULT

+CBaseOutputPin::CheckConnect(IPin * pPin)

+{

+    HRESULT hr = CBasePin::CheckConnect(pPin);

+    if (FAILED(hr)) {

+    return hr;

+    }

+

+    // get an input pin and an allocator interface

+    hr = pPin->QueryInterface(IID_IMemInputPin, (void **) &m_pInputPin);

+    if (FAILED(hr)) {

+        return hr;

+    }

+    return NOERROR;

+}

+

+

+/* Overriden from CBasePin */

+

+HRESULT

+CBaseOutputPin::BreakConnect()

+{

+    /* Release any allocator we hold */

+

+    if (m_pAllocator) {

+        // Always decommit the allocator because a downstream filter may or

+        // may not decommit the connection's allocator.  A memory leak could

+        // occur if the allocator is not decommited when a connection is broken.

+        HRESULT hr = m_pAllocator->Decommit();

+        if( FAILED( hr ) ) {

+            return hr;

+        }

+

+        m_pAllocator->Release();

+        m_pAllocator = NULL;

+    }

+

+    /* Release any input pin interface we hold */

+

+    if (m_pInputPin) {

+        m_pInputPin->Release();

+        m_pInputPin = NULL;

+    }

+    return NOERROR;

+}

+

+

+/* This is called when the input pin didn't give us a valid allocator */

+

+HRESULT

+CBaseOutputPin::InitAllocator(__deref_out IMemAllocator **ppAlloc)

+{

+    return CreateMemoryAllocator(ppAlloc);

+}

+

+

+/* Decide on an allocator, override this if you want to use your own allocator

+   Override DecideBufferSize to call SetProperties. If the input pin fails

+   the GetAllocator call then this will construct a CMemAllocator and call

+   DecideBufferSize on that, and if that fails then we are completely hosed.

+   If the you succeed the DecideBufferSize call, we will notify the input

+   pin of the selected allocator. NOTE this is called during Connect() which

+   therefore looks after grabbing and locking the object's critical section */

+

+// We query the input pin for its requested properties and pass this to

+// DecideBufferSize to allow it to fulfill requests that it is happy

+// with (eg most people don't care about alignment and are thus happy to

+// use the downstream pin's alignment request).

+

+HRESULT

+CBaseOutputPin::DecideAllocator(IMemInputPin *pPin, __deref_out IMemAllocator **ppAlloc)

+{

+    HRESULT hr = NOERROR;

+    *ppAlloc = NULL;

+

+    // get downstream prop request

+    // the derived class may modify this in DecideBufferSize, but

+    // we assume that he will consistently modify it the same way,

+    // so we only get it once

+    ALLOCATOR_PROPERTIES prop;

+    ZeroMemory(&prop, sizeof(prop));

+

+    // whatever he returns, we assume prop is either all zeros

+    // or he has filled it out.

+    pPin->GetAllocatorRequirements(&prop);

+

+    // if he doesn't care about alignment, then set it to 1

+    if (prop.cbAlign == 0) {

+        prop.cbAlign = 1;

+    }

+

+    /* Try the allocator provided by the input pin */

+

+    hr = pPin->GetAllocator(ppAlloc);

+    if (SUCCEEDED(hr)) {

+

+        hr = DecideBufferSize(*ppAlloc, &prop);

+        if (SUCCEEDED(hr)) {

+            hr = pPin->NotifyAllocator(*ppAlloc, FALSE);

+            if (SUCCEEDED(hr)) {

+                return NOERROR;

+            }

+        }

+    }

+

+    /* If the GetAllocator failed we may not have an interface */

+

+    if (*ppAlloc) {

+        (*ppAlloc)->Release();

+        *ppAlloc = NULL;

+    }

+

+    /* Try the output pin's allocator by the same method */

+

+    hr = InitAllocator(ppAlloc);

+    if (SUCCEEDED(hr)) {

+

+        // note - the properties passed here are in the same

+        // structure as above and may have been modified by

+        // the previous call to DecideBufferSize

+        hr = DecideBufferSize(*ppAlloc, &prop);

+        if (SUCCEEDED(hr)) {

+            hr = pPin->NotifyAllocator(*ppAlloc, FALSE);

+            if (SUCCEEDED(hr)) {

+                return NOERROR;

+            }

+        }

+    }

+

+    /* Likewise we may not have an interface to release */

+

+    if (*ppAlloc) {

+        (*ppAlloc)->Release();

+        *ppAlloc = NULL;

+    }

+    return hr;

+}

+

+

+/* This returns an empty sample buffer from the allocator WARNING the same

+   dangers and restrictions apply here as described below for Deliver() */

+

+HRESULT

+CBaseOutputPin::GetDeliveryBuffer(__deref_out IMediaSample ** ppSample,

+                                  __in_opt REFERENCE_TIME * pStartTime,

+                                  __in_opt REFERENCE_TIME * pEndTime,

+                                  DWORD dwFlags)

+{

+    if (m_pAllocator != NULL) {

+        return m_pAllocator->GetBuffer(ppSample,pStartTime,pEndTime,dwFlags);

+    } else {

+        return E_NOINTERFACE;

+    }

+}

+

+

+/* Deliver a filled-in sample to the connected input pin. NOTE the object must

+   have locked itself before calling us otherwise we may get halfway through

+   executing this method only to find the filter graph has got in and

+   disconnected us from the input pin. If the filter has no worker threads

+   then the lock is best applied on Receive(), otherwise it should be done

+   when the worker thread is ready to deliver. There is a wee snag to worker

+   threads that this shows up. The worker thread must lock the object when

+   it is ready to deliver a sample, but it may have to wait until a state

+   change has completed, but that may never complete because the state change

+   is waiting for the worker thread to complete. The way to handle this is for

+   the state change code to grab the critical section, then set an abort event

+   for the worker thread, then release the critical section and wait for the

+   worker thread to see the event we set and then signal that it has finished

+   (with another event). At which point the state change code can complete */

+

+// note (if you've still got any breath left after reading that) that you

+// need to release the sample yourself after this call. if the connected

+// input pin needs to hold onto the sample beyond the call, it will addref

+// the sample itself.

+

+// of course you must release this one and call GetDeliveryBuffer for the

+// next. You cannot reuse it directly.

+

+HRESULT

+CBaseOutputPin::Deliver(IMediaSample * pSample)

+{

+    if (m_pInputPin == NULL) {

+        return VFW_E_NOT_CONNECTED;

+    }

+

+#ifdef DXMPERF

+    PERFLOG_DELIVER( m_pName ? m_pName : L"CBaseOutputPin", (IPin *) this, (IPin  *) m_pInputPin, pSample, &m_mt );

+#endif // DXMPERF

+

+    return m_pInputPin->Receive(pSample);

+}

+

+

+// called from elsewhere in our filter to pass EOS downstream to

+// our connected input pin

+HRESULT

+CBaseOutputPin::DeliverEndOfStream(void)

+{

+    // remember this is on IPin not IMemInputPin

+    if (m_Connected == NULL) {

+        return VFW_E_NOT_CONNECTED;

+    }

+    return m_Connected->EndOfStream();

+}

+

+

+/* Commit the allocator's memory, this is called through IMediaFilter

+   which is responsible for locking the object before calling us */

+

+HRESULT

+CBaseOutputPin::Active(void)

+{

+    if (m_pAllocator == NULL) {

+        return VFW_E_NO_ALLOCATOR;

+    }

+    return m_pAllocator->Commit();

+}

+

+

+/* Free up or unprepare allocator's memory, this is called through

+   IMediaFilter which is responsible for locking the object first */

+

+HRESULT

+CBaseOutputPin::Inactive(void)

+{

+    m_bRunTimeError = FALSE;

+    if (m_pAllocator == NULL) {

+        return VFW_E_NO_ALLOCATOR;

+    }

+    return m_pAllocator->Decommit();

+}

+

+// we have a default handling of EndOfStream which is to return

+// an error, since this should be called on input pins only

+STDMETHODIMP

+CBaseOutputPin::EndOfStream(void)

+{

+    return E_UNEXPECTED;

+}

+

+

+// BeginFlush should be called on input pins only

+STDMETHODIMP

+CBaseOutputPin::BeginFlush(void)

+{

+    return E_UNEXPECTED;

+}

+

+// EndFlush should be called on input pins only

+STDMETHODIMP

+CBaseOutputPin::EndFlush(void)

+{

+    return E_UNEXPECTED;

+}

+

+// call BeginFlush on the connected input pin

+HRESULT

+CBaseOutputPin::DeliverBeginFlush(void)

+{

+    // remember this is on IPin not IMemInputPin

+    if (m_Connected == NULL) {

+        return VFW_E_NOT_CONNECTED;

+    }

+    return m_Connected->BeginFlush();

+}

+

+// call EndFlush on the connected input pin

+HRESULT

+CBaseOutputPin::DeliverEndFlush(void)

+{

+    // remember this is on IPin not IMemInputPin

+    if (m_Connected == NULL) {

+        return VFW_E_NOT_CONNECTED;

+    }

+    return m_Connected->EndFlush();

+}

+// deliver NewSegment to connected pin

+HRESULT

+CBaseOutputPin::DeliverNewSegment(

+    REFERENCE_TIME tStart,

+    REFERENCE_TIME tStop,

+    double dRate)

+{

+    if (m_Connected == NULL) {

+        return VFW_E_NOT_CONNECTED;

+    }

+    return m_Connected->NewSegment(tStart, tStop, dRate);

+}

+

+

+//=====================================================================

+//=====================================================================

+// Implements CBaseInputPin

+//=====================================================================

+//=====================================================================

+

+

+/* Constructor creates a default allocator object */

+

+CBaseInputPin::CBaseInputPin(__in_opt LPCTSTR pObjectName,

+                 __in CBaseFilter *pFilter,

+                 __in CCritSec *pLock,

+                 __inout HRESULT *phr,

+                 __in_opt LPCWSTR pPinName) :

+    CBasePin(pObjectName, pFilter, pLock, phr, pPinName, PINDIR_INPUT),

+    m_pAllocator(NULL),

+    m_bReadOnly(FALSE),

+    m_bFlushing(FALSE)

+{

+    ZeroMemory(&m_SampleProps, sizeof(m_SampleProps));

+}

+

+#ifdef UNICODE

+CBaseInputPin::CBaseInputPin(__in LPCSTR pObjectName,

+                 __in CBaseFilter *pFilter,

+                 __in CCritSec *pLock,

+                 __inout HRESULT *phr,

+                 __in_opt LPCWSTR pPinName) :

+    CBasePin(pObjectName, pFilter, pLock, phr, pPinName, PINDIR_INPUT),

+    m_pAllocator(NULL),

+    m_bReadOnly(FALSE),

+    m_bFlushing(FALSE)

+{

+    ZeroMemory(&m_SampleProps, sizeof(m_SampleProps));

+}

+#endif

+

+/* Destructor releases it's reference count on the default allocator */

+

+CBaseInputPin::~CBaseInputPin()

+{

+    if (m_pAllocator != NULL) {

+    m_pAllocator->Release();

+    m_pAllocator = NULL;

+    }

+}

+

+

+// override this to publicise our interfaces

+STDMETHODIMP

+CBaseInputPin::NonDelegatingQueryInterface(REFIID riid, __deref_out void **ppv)

+{

+    /* Do we know about this interface */

+

+    if (riid == IID_IMemInputPin) {

+        return GetInterface((IMemInputPin *) this, ppv);

+    } else {

+        return CBasePin::NonDelegatingQueryInterface(riid, ppv);

+    }

+}

+

+

+/* Return the allocator interface that this input pin would like the output

+   pin to use. NOTE subsequent calls to GetAllocator should all return an

+   interface onto the SAME object so we create one object at the start

+

+   Note:

+       The allocator is Release()'d on disconnect and replaced on

+       NotifyAllocator().

+

+   Override this to provide your own allocator.

+*/

+

+STDMETHODIMP

+CBaseInputPin::GetAllocator(

+    __deref_out IMemAllocator **ppAllocator)

+{

+    CheckPointer(ppAllocator,E_POINTER);

+    ValidateReadWritePtr(ppAllocator,sizeof(IMemAllocator *));

+    CAutoLock cObjectLock(m_pLock);

+

+    if (m_pAllocator == NULL) {

+        HRESULT hr = CreateMemoryAllocator(&m_pAllocator);

+        if (FAILED(hr)) {

+            return hr;

+        }

+    }

+    ASSERT(m_pAllocator != NULL);

+    *ppAllocator = m_pAllocator;

+    m_pAllocator->AddRef();

+    return NOERROR;

+}

+

+

+/* Tell the input pin which allocator the output pin is actually going to use

+   Override this if you care - NOTE the locking we do both here and also in

+   GetAllocator is unnecessary but derived classes that do something useful

+   will undoubtedly have to lock the object so this might help remind people */

+

+STDMETHODIMP

+CBaseInputPin::NotifyAllocator(

+    IMemAllocator * pAllocator,

+    BOOL bReadOnly)

+{

+    CheckPointer(pAllocator,E_POINTER);

+    ValidateReadPtr(pAllocator,sizeof(IMemAllocator));

+    CAutoLock cObjectLock(m_pLock);

+

+    IMemAllocator *pOldAllocator = m_pAllocator;

+    pAllocator->AddRef();

+    m_pAllocator = pAllocator;

+

+    if (pOldAllocator != NULL) {

+        pOldAllocator->Release();

+    }

+

+    // the readonly flag indicates whether samples from this allocator should

+    // be regarded as readonly - if true, then inplace transforms will not be

+    // allowed.

+    m_bReadOnly = (BYTE)bReadOnly;

+    return NOERROR;

+}

+

+

+HRESULT

+CBaseInputPin::BreakConnect()

+{

+    /* We don't need our allocator any more */

+    if (m_pAllocator) {

+        // Always decommit the allocator because a downstream filter may or

+        // may not decommit the connection's allocator.  A memory leak could

+        // occur if the allocator is not decommited when a pin is disconnected.

+        HRESULT hr = m_pAllocator->Decommit();

+        if( FAILED( hr ) ) {

+            return hr;

+        }

+

+        m_pAllocator->Release();

+        m_pAllocator = NULL;

+    }

+

+    return S_OK;

+}

+

+

+/* Do something with this media sample - this base class checks to see if the

+   format has changed with this media sample and if so checks that the filter

+   will accept it, generating a run time error if not. Once we have raised a

+   run time error we set a flag so that no more samples will be accepted

+

+   It is important that any filter should override this method and implement

+   synchronization so that samples are not processed when the pin is

+   disconnected etc

+*/

+

+STDMETHODIMP

+CBaseInputPin::Receive(IMediaSample *pSample)

+{

+    CheckPointer(pSample,E_POINTER);

+    ValidateReadPtr(pSample,sizeof(IMediaSample));

+    ASSERT(pSample);

+

+    HRESULT hr = CheckStreaming();

+    if (S_OK != hr) {

+        return hr;

+    }

+

+#ifdef DXMPERF

+    PERFLOG_RECEIVE( m_pName ? m_pName : L"CBaseInputPin", (IPin *) m_Connected, (IPin *) this, pSample, &m_mt );

+#endif // DXMPERF

+

+

+    /* Check for IMediaSample2 */

+    IMediaSample2 *pSample2;

+    if (SUCCEEDED(pSample->QueryInterface(IID_IMediaSample2, (void **)&pSample2))) {

+        hr = pSample2->GetProperties(sizeof(m_SampleProps), (PBYTE)&m_SampleProps);

+        pSample2->Release();

+        if (FAILED(hr)) {

+            return hr;

+        }

+    } else {

+        /*  Get the properties the hard way */

+        m_SampleProps.cbData = sizeof(m_SampleProps);

+        m_SampleProps.dwTypeSpecificFlags = 0;

+        m_SampleProps.dwStreamId = AM_STREAM_MEDIA;

+        m_SampleProps.dwSampleFlags = 0;

+        if (S_OK == pSample->IsDiscontinuity()) {

+            m_SampleProps.dwSampleFlags |= AM_SAMPLE_DATADISCONTINUITY;

+        }

+        if (S_OK == pSample->IsPreroll()) {

+            m_SampleProps.dwSampleFlags |= AM_SAMPLE_PREROLL;

+        }

+        if (S_OK == pSample->IsSyncPoint()) {

+            m_SampleProps.dwSampleFlags |= AM_SAMPLE_SPLICEPOINT;

+        }

+        if (SUCCEEDED(pSample->GetTime(&m_SampleProps.tStart,

+                                       &m_SampleProps.tStop))) {

+            m_SampleProps.dwSampleFlags |= AM_SAMPLE_TIMEVALID |

+                                           AM_SAMPLE_STOPVALID;

+        }

+        if (S_OK == pSample->GetMediaType(&m_SampleProps.pMediaType)) {

+            m_SampleProps.dwSampleFlags |= AM_SAMPLE_TYPECHANGED;

+        }

+        pSample->GetPointer(&m_SampleProps.pbBuffer);

+        m_SampleProps.lActual = pSample->GetActualDataLength();

+        m_SampleProps.cbBuffer = pSample->GetSize();

+    }

+

+    /* Has the format changed in this sample */

+

+    if (!(m_SampleProps.dwSampleFlags & AM_SAMPLE_TYPECHANGED)) {

+        return NOERROR;

+    }

+

+    /* Check the derived class accepts this format */

+    /* This shouldn't fail as the source must call QueryAccept first */

+

+    hr = CheckMediaType((CMediaType *)m_SampleProps.pMediaType);

+

+    if (hr == NOERROR) {

+        return NOERROR;

+    }

+

+    /* Raise a runtime error if we fail the media type */

+

+    m_bRunTimeError = TRUE;

+    EndOfStream();

+    m_pFilter->NotifyEvent(EC_ERRORABORT,VFW_E_TYPE_NOT_ACCEPTED,0);

+    return VFW_E_INVALIDMEDIATYPE;

+}

+

+

+/*  Receive multiple samples */

+STDMETHODIMP

+CBaseInputPin::ReceiveMultiple (

+    __in_ecount(nSamples) IMediaSample **pSamples,

+    long nSamples,

+    __out long *nSamplesProcessed)

+{

+    CheckPointer(pSamples,E_POINTER);

+    ValidateReadPtr(pSamples,nSamples * sizeof(IMediaSample *));

+

+    HRESULT hr = S_OK;

+    *nSamplesProcessed = 0;

+    while (nSamples-- > 0) {

+         hr = Receive(pSamples[*nSamplesProcessed]);

+

+         /*  S_FALSE means don't send any more */

+         if (hr != S_OK) {

+             break;

+         }

+         (*nSamplesProcessed)++;

+    }

+    return hr;

+}

+

+/*  See if Receive() might block */

+STDMETHODIMP

+CBaseInputPin::ReceiveCanBlock()

+{

+    /*  Ask all the output pins if they block

+        If there are no output pin assume we do block

+    */

+    int cPins = m_pFilter->GetPinCount();

+    int cOutputPins = 0;

+    for (int c = 0; c < cPins; c++) {

+        CBasePin *pPin = m_pFilter->GetPin(c);

+        if (NULL == pPin) {

+            break;

+        }

+        PIN_DIRECTION pd;

+        HRESULT hr = pPin->QueryDirection(&pd);

+        if (FAILED(hr)) {

+            return hr;

+        }

+

+        if (pd == PINDIR_OUTPUT) {

+

+            IPin *pConnected;

+            hr = pPin->ConnectedTo(&pConnected);

+            if (SUCCEEDED(hr)) {

+                ASSERT(pConnected != NULL);

+                cOutputPins++;

+                IMemInputPin *pInputPin;

+                hr = pConnected->QueryInterface(

+                                              IID_IMemInputPin,

+                                              (void **)&pInputPin);

+                pConnected->Release();

+                if (SUCCEEDED(hr)) {

+                    hr = pInputPin->ReceiveCanBlock();

+                    pInputPin->Release();

+                    if (hr != S_FALSE) {

+                        return S_OK;

+                    }

+                } else {

+                    /*  There's a transport we don't understand here */

+                    return S_OK;

+                }

+            }

+        }

+    }

+    return cOutputPins == 0 ? S_OK : S_FALSE;

+}

+

+// Default handling for BeginFlush - call at the beginning

+// of your implementation (makes sure that all Receive calls

+// fail). After calling this, you need to free any queued data

+// and then call downstream.

+STDMETHODIMP

+CBaseInputPin::BeginFlush(void)

+{

+    //  BeginFlush is NOT synchronized with streaming but is part of

+    //  a control action - hence we synchronize with the filter

+    CAutoLock lck(m_pLock);

+

+    // if we are already in mid-flush, this is probably a mistake

+    // though not harmful - try to pick it up for now so I can think about it

+    ASSERT(!m_bFlushing);

+

+    // first thing to do is ensure that no further Receive calls succeed

+    m_bFlushing = TRUE;

+

+    // now discard any data and call downstream - must do that

+    // in derived classes

+    return S_OK;

+}

+

+// default handling for EndFlush - call at end of your implementation

+// - before calling this, ensure that there is no queued data and no thread

+// pushing any more without a further receive, then call downstream,

+// then call this method to clear the m_bFlushing flag and re-enable

+// receives

+STDMETHODIMP

+CBaseInputPin::EndFlush(void)

+{

+    //  Endlush is NOT synchronized with streaming but is part of

+    //  a control action - hence we synchronize with the filter

+    CAutoLock lck(m_pLock);

+

+    // almost certainly a mistake if we are not in mid-flush

+    ASSERT(m_bFlushing);

+

+    // before calling, sync with pushing thread and ensure

+    // no more data is going downstream, then call EndFlush on

+    // downstream pins.

+

+    // now re-enable Receives

+    m_bFlushing = FALSE;

+

+    // No more errors

+    m_bRunTimeError = FALSE;

+

+    return S_OK;

+}

+

+

+STDMETHODIMP

+CBaseInputPin::Notify(IBaseFilter * pSender, Quality q)

+{

+    UNREFERENCED_PARAMETER(q);

+    CheckPointer(pSender,E_POINTER);

+    ValidateReadPtr(pSender,sizeof(IBaseFilter));

+    DbgBreak("IQuality::Notify called on an input pin");

+    return NOERROR;

+} // Notify

+

+/* Free up or unprepare allocator's memory, this is called through

+   IMediaFilter which is responsible for locking the object first */

+

+HRESULT

+CBaseInputPin::Inactive(void)

+{

+    m_bRunTimeError = FALSE;

+    if (m_pAllocator == NULL) {

+        return VFW_E_NO_ALLOCATOR;

+    }

+

+    m_bFlushing = FALSE;

+

+    return m_pAllocator->Decommit();

+}

+

+// what requirements do we have of the allocator - override if you want

+// to support other people's allocators but need a specific alignment

+// or prefix.

+STDMETHODIMP

+CBaseInputPin::GetAllocatorRequirements(__out ALLOCATOR_PROPERTIES*pProps)

+{

+    UNREFERENCED_PARAMETER(pProps);

+    return E_NOTIMPL;

+}

+

+//  Check if it's OK to process data

+//

+HRESULT

+CBaseInputPin::CheckStreaming()

+{

+    //  Shouldn't be able to get any data if we're not connected!

+    ASSERT(IsConnected());

+

+    //  Don't process stuff in Stopped state

+    if (IsStopped()) {

+        return VFW_E_WRONG_STATE;

+    }

+    if (m_bFlushing) {

+        return S_FALSE;

+    }

+    if (m_bRunTimeError) {

+        return VFW_E_RUNTIME_ERROR;

+    }

+    return S_OK;

+}

+

+// Pass on the Quality notification q to

+// a. Our QualityControl sink (if we have one) or else

+// b. to our upstream filter

+// and if that doesn't work, throw it away with a bad return code

+HRESULT

+CBaseInputPin::PassNotify(Quality& q)

+{

+    // We pass the message on, which means that we find the quality sink

+    // for our input pin and send it there

+

+    DbgLog((LOG_TRACE,3,TEXT("Passing Quality notification through transform")));

+    if (m_pQSink!=NULL) {

+        return m_pQSink->Notify(m_pFilter, q);

+    } else {

+        // no sink set, so pass it upstream

+        HRESULT hr;

+        IQualityControl * pIQC;

+

+        hr = VFW_E_NOT_FOUND;                   // default

+        if (m_Connected) {

+            m_Connected->QueryInterface(IID_IQualityControl, (void**)&pIQC);

+

+            if (pIQC!=NULL) {

+                hr = pIQC->Notify(m_pFilter, q);

+                pIQC->Release();

+            }

+        }

+        return hr;

+    }

+

+} // PassNotify

+

+//=====================================================================

+//=====================================================================

+// Memory allocation class, implements CMediaSample

+//=====================================================================

+//=====================================================================

+

+

+/* NOTE The implementation of this class calls the CUnknown constructor with

+   a NULL outer unknown pointer. This has the effect of making us a self

+   contained class, ie any QueryInterface, AddRef or Release calls will be

+   routed to the class's NonDelegatingUnknown methods. You will typically

+   find that the classes that do this then override one or more of these

+   virtual functions to provide more specialised behaviour. A good example

+   of this is where a class wants to keep the QueryInterface internal but

+   still wants it's lifetime controlled by the external object */

+

+/* The last two parameters have default values of NULL and zero */

+

+CMediaSample::CMediaSample(__in_opt LPCTSTR pName,

+               __in_opt CBaseAllocator *pAllocator,

+               __inout_opt HRESULT *phr,

+               __in_bcount_opt(length) LPBYTE pBuffer,

+               LONG length) :

+    m_pBuffer(pBuffer),             // Initialise the buffer

+    m_cbBuffer(length),             // And it's length

+    m_lActual(length),              // By default, actual = length

+    m_pMediaType(NULL),             // No media type change

+    m_dwFlags(0),                   // Nothing set

+    m_cRef(0),                      // 0 ref count

+    m_dwTypeSpecificFlags(0),       // Type specific flags

+    m_dwStreamId(AM_STREAM_MEDIA),  // Stream id

+    m_pAllocator(pAllocator)        // Allocator

+{

+#ifdef DXMPERF

+    PERFLOG_CTOR( pName ? pName : L"CMediaSample", (IMediaSample *) this );

+#endif // DXMPERF

+

+    /* We must have an owner and it must also be derived from class

+       CBaseAllocator BUT we do not hold a reference count on it */

+

+    ASSERT(pAllocator);

+

+    if (length < 0) {

+        *phr = VFW_E_BUFFER_OVERFLOW;

+        m_cbBuffer = 0;

+    }

+}

+

+#ifdef UNICODE

+CMediaSample::CMediaSample(__in_opt LPCSTR pName,

+               __in_opt CBaseAllocator *pAllocator,

+               __inout_opt HRESULT *phr,

+               __in_bcount_opt(length) LPBYTE pBuffer,

+               LONG length) :

+    m_pBuffer(pBuffer),             // Initialise the buffer

+    m_cbBuffer(length),             // And it's length

+    m_lActual(length),              // By default, actual = length

+    m_pMediaType(NULL),             // No media type change

+    m_dwFlags(0),                   // Nothing set

+    m_cRef(0),                      // 0 ref count

+    m_dwTypeSpecificFlags(0),       // Type specific flags

+    m_dwStreamId(AM_STREAM_MEDIA),  // Stream id

+    m_pAllocator(pAllocator)        // Allocator

+{

+#ifdef DXMPERF

+    PERFLOG_CTOR( L"CMediaSample", (IMediaSample *) this );

+#endif // DXMPERF

+

+    /* We must have an owner and it must also be derived from class

+       CBaseAllocator BUT we do not hold a reference count on it */

+

+    ASSERT(pAllocator);

+}

+#endif

+

+/* Destructor deletes the media type memory */

+

+CMediaSample::~CMediaSample()

+{

+#ifdef DXMPERF

+    PERFLOG_DTOR( L"CMediaSample", (IMediaSample *) this );

+#endif // DXMPERF

+

+    if (m_pMediaType) {

+    DeleteMediaType(m_pMediaType);

+    }

+}

+

+/* Override this to publicise our interfaces */

+

+STDMETHODIMP

+CMediaSample::QueryInterface(REFIID riid, __deref_out void **ppv)

+{

+    if (riid == IID_IMediaSample ||

+        riid == IID_IMediaSample2 ||

+        riid == IID_IUnknown) {

+        return GetInterface((IMediaSample *) this, ppv);

+    } else {

+        *ppv = NULL;

+        return E_NOINTERFACE;

+    }

+}

+

+STDMETHODIMP_(ULONG)

+CMediaSample::AddRef()

+{

+    return InterlockedIncrement(&m_cRef);

+}

+

+

+// --  CMediaSample lifetimes --

+//

+// On final release of this sample buffer it is not deleted but

+// returned to the freelist of the owning memory allocator

+//

+// The allocator may be waiting for the last buffer to be placed on the free

+// list in order to decommit all the memory, so the ReleaseBuffer() call may

+// result in this sample being deleted. We also need to hold a refcount on

+// the allocator to stop that going away until we have finished with this.

+// However, we cannot release the allocator before the ReleaseBuffer, as the

+// release may cause us to be deleted. Similarly we can't do it afterwards.

+//

+// Thus we must leave it to the allocator to hold an addref on our behalf.

+// When he issues us in GetBuffer, he addref's himself. When ReleaseBuffer

+// is called, he releases himself, possibly causing us and him to be deleted.

+

+

+STDMETHODIMP_(ULONG)

+CMediaSample::Release()

+{

+    /* Decrement our own private reference count */

+    LONG lRef;

+    if (m_cRef == 1) {

+        lRef = 0;

+        m_cRef = 0;

+    } else {

+        lRef = InterlockedDecrement(&m_cRef);

+    }

+    ASSERT(lRef >= 0);

+

+    DbgLog((LOG_MEMORY,3,TEXT("    Unknown %X ref-- = %d"),

+        this, m_cRef));

+

+    /* Did we release our final reference count */

+    if (lRef == 0) {

+        /* Free all resources */

+        if (m_dwFlags & Sample_TypeChanged) {

+            SetMediaType(NULL);

+        }

+        ASSERT(m_pMediaType == NULL);

+        m_dwFlags = 0;

+        m_dwTypeSpecificFlags = 0;

+        m_dwStreamId = AM_STREAM_MEDIA;

+

+        /* This may cause us to be deleted */

+        // Our refcount is reliably 0 thus no-one will mess with us

+        m_pAllocator->ReleaseBuffer(this);

+    }

+    return (ULONG)lRef;

+}

+

+

+// set the buffer pointer and length. Used by allocators that

+// want variable sized pointers or pointers into already-read data.

+// This is only available through a CMediaSample* not an IMediaSample*

+// and so cannot be changed by clients.

+HRESULT

+CMediaSample::SetPointer(__in_bcount(cBytes) BYTE * ptr, LONG cBytes)

+{

+    if (cBytes < 0) {

+        return VFW_E_BUFFER_OVERFLOW;

+    }

+    m_pBuffer = ptr;            // new buffer area (could be null)

+    m_cbBuffer = cBytes;        // length of buffer

+    m_lActual = cBytes;         // length of data in buffer (assume full)

+

+    return S_OK;

+}

+

+

+// get me a read/write pointer to this buffer's memory. I will actually

+// want to use sizeUsed bytes.

+STDMETHODIMP

+CMediaSample::GetPointer(__deref_out BYTE ** ppBuffer)

+{

+    ValidateReadWritePtr(ppBuffer,sizeof(BYTE *));

+

+    // creator must have set pointer either during

+    // constructor or by SetPointer

+    ASSERT(m_pBuffer);

+

+    *ppBuffer = m_pBuffer;

+    return NOERROR;

+}

+

+

+// return the size in bytes of this buffer

+STDMETHODIMP_(LONG)

+CMediaSample::GetSize(void)

+{

+    return m_cbBuffer;

+}

+

+

+// get the stream time at which this sample should start and finish.

+STDMETHODIMP

+CMediaSample::GetTime(

+    __out REFERENCE_TIME * pTimeStart,     // put time here

+    __out REFERENCE_TIME * pTimeEnd

+)

+{

+    ValidateReadWritePtr(pTimeStart,sizeof(REFERENCE_TIME));

+    ValidateReadWritePtr(pTimeEnd,sizeof(REFERENCE_TIME));

+

+    if (!(m_dwFlags & Sample_StopValid)) {

+        if (!(m_dwFlags & Sample_TimeValid)) {

+            return VFW_E_SAMPLE_TIME_NOT_SET;

+        } else {

+            *pTimeStart = m_Start;

+

+            //  Make sure old stuff works

+            *pTimeEnd = m_Start + 1;

+            return VFW_S_NO_STOP_TIME;

+        }

+    }

+

+    *pTimeStart = m_Start;

+    *pTimeEnd = m_End;

+    return NOERROR;

+}

+

+

+// Set the stream time at which this sample should start and finish.

+// NULL pointers means the time is reset

+STDMETHODIMP

+CMediaSample::SetTime(

+    __in_opt REFERENCE_TIME * pTimeStart,

+    __in_opt REFERENCE_TIME * pTimeEnd

+)

+{

+    if (pTimeStart == NULL) {

+        ASSERT(pTimeEnd == NULL);

+        m_dwFlags &= ~(Sample_TimeValid | Sample_StopValid);

+    } else {

+        if (pTimeEnd == NULL) {

+            m_Start = *pTimeStart;

+            m_dwFlags |= Sample_TimeValid;

+            m_dwFlags &= ~Sample_StopValid;

+        } else {

+            ValidateReadPtr(pTimeStart,sizeof(REFERENCE_TIME));

+            ValidateReadPtr(pTimeEnd,sizeof(REFERENCE_TIME));

+            ASSERT(*pTimeEnd >= *pTimeStart);

+

+            m_Start = *pTimeStart;

+            m_End = *pTimeEnd;

+            m_dwFlags |= Sample_TimeValid | Sample_StopValid;

+        }

+    }

+    return NOERROR;

+}

+

+

+// get the media times (eg bytes) for this sample

+STDMETHODIMP

+CMediaSample::GetMediaTime(

+    __out LONGLONG * pTimeStart,

+    __out LONGLONG * pTimeEnd

+)

+{

+    ValidateReadWritePtr(pTimeStart,sizeof(LONGLONG));

+    ValidateReadWritePtr(pTimeEnd,sizeof(LONGLONG));

+

+    if (!(m_dwFlags & Sample_MediaTimeValid)) {

+        return VFW_E_MEDIA_TIME_NOT_SET;

+    }

+

+    *pTimeStart = m_MediaStart;

+    *pTimeEnd = (m_MediaStart + m_MediaEnd);

+    return NOERROR;

+}

+

+

+// Set the media times for this sample

+STDMETHODIMP

+CMediaSample::SetMediaTime(

+    __in_opt LONGLONG * pTimeStart,

+    __in_opt LONGLONG * pTimeEnd

+)

+{

+    if (pTimeStart == NULL) {

+        ASSERT(pTimeEnd == NULL);

+        m_dwFlags &= ~Sample_MediaTimeValid;

+    } else {

+        if (NULL == pTimeEnd) {

+            return E_POINTER;

+        }

+        ValidateReadPtr(pTimeStart,sizeof(LONGLONG));

+        ValidateReadPtr(pTimeEnd,sizeof(LONGLONG));

+        ASSERT(*pTimeEnd >= *pTimeStart);

+

+        m_MediaStart = *pTimeStart;

+        m_MediaEnd = (LONG)(*pTimeEnd - *pTimeStart);

+        m_dwFlags |= Sample_MediaTimeValid;

+    }

+    return NOERROR;

+}

+

+

+STDMETHODIMP

+CMediaSample::IsSyncPoint(void)

+{

+    if (m_dwFlags & Sample_SyncPoint) {

+        return S_OK;

+    } else {

+        return S_FALSE;

+    }

+}

+

+

+STDMETHODIMP

+CMediaSample::SetSyncPoint(BOOL bIsSyncPoint)

+{

+    if (bIsSyncPoint) {

+        m_dwFlags |= Sample_SyncPoint;

+    } else {

+        m_dwFlags &= ~Sample_SyncPoint;

+    }

+    return NOERROR;

+}

+

+// returns S_OK if there is a discontinuity in the data (this same is

+// not a continuation of the previous stream of data

+// - there has been a seek).

+STDMETHODIMP

+CMediaSample::IsDiscontinuity(void)

+{

+    if (m_dwFlags & Sample_Discontinuity) {

+        return S_OK;

+    } else {

+        return S_FALSE;

+    }

+}

+

+// set the discontinuity property - TRUE if this sample is not a

+// continuation, but a new sample after a seek.

+STDMETHODIMP

+CMediaSample::SetDiscontinuity(BOOL bDiscont)

+{

+    // should be TRUE or FALSE

+    if (bDiscont) {

+        m_dwFlags |= Sample_Discontinuity;

+    } else {

+        m_dwFlags &= ~Sample_Discontinuity;

+    }

+    return S_OK;

+}

+

+STDMETHODIMP

+CMediaSample::IsPreroll(void)

+{

+    if (m_dwFlags & Sample_Preroll) {

+        return S_OK;

+    } else {

+        return S_FALSE;

+    }

+}

+

+

+STDMETHODIMP

+CMediaSample::SetPreroll(BOOL bIsPreroll)

+{

+    if (bIsPreroll) {

+        m_dwFlags |= Sample_Preroll;

+    } else {

+        m_dwFlags &= ~Sample_Preroll;

+    }

+    return NOERROR;

+}

+

+STDMETHODIMP_(LONG)

+CMediaSample::GetActualDataLength(void)

+{

+    return m_lActual;

+}

+

+

+STDMETHODIMP

+CMediaSample::SetActualDataLength(LONG lActual)

+{

+    if (lActual > m_cbBuffer || lActual < 0) {

+        ASSERT(lActual <= GetSize());

+        return VFW_E_BUFFER_OVERFLOW;

+    }

+    m_lActual = lActual;

+    return NOERROR;

+}

+

+

+/* These allow for limited format changes in band */

+

+STDMETHODIMP

+CMediaSample::GetMediaType(__deref_out AM_MEDIA_TYPE **ppMediaType)

+{

+    ValidateReadWritePtr(ppMediaType,sizeof(AM_MEDIA_TYPE *));

+    ASSERT(ppMediaType);

+

+    /* Do we have a new media type for them */

+

+    if (!(m_dwFlags & Sample_TypeChanged)) {

+        ASSERT(m_pMediaType == NULL);

+        *ppMediaType = NULL;

+        return S_FALSE;

+    }

+

+    ASSERT(m_pMediaType);

+

+    /* Create a copy of our media type */

+

+    *ppMediaType = CreateMediaType(m_pMediaType);

+    if (*ppMediaType == NULL) {

+        return E_OUTOFMEMORY;

+    }

+    return NOERROR;

+}

+

+

+/* Mark this sample as having a different format type */

+

+STDMETHODIMP

+CMediaSample::SetMediaType(__in_opt AM_MEDIA_TYPE *pMediaType)

+{

+    /* Delete the current media type */

+

+    if (m_pMediaType) {

+        DeleteMediaType(m_pMediaType);

+        m_pMediaType = NULL;

+    }

+

+    /* Mechanism for resetting the format type */

+

+    if (pMediaType == NULL) {

+        m_dwFlags &= ~Sample_TypeChanged;

+        return NOERROR;

+    }

+

+    ASSERT(pMediaType);

+    ValidateReadPtr(pMediaType,sizeof(AM_MEDIA_TYPE));

+

+    /* Take a copy of the media type */

+

+    m_pMediaType = CreateMediaType(pMediaType);

+    if (m_pMediaType == NULL) {

+        m_dwFlags &= ~Sample_TypeChanged;

+        return E_OUTOFMEMORY;

+    }

+

+    m_dwFlags |= Sample_TypeChanged;

+    return NOERROR;

+}

+

+// Set and get properties (IMediaSample2)

+STDMETHODIMP CMediaSample::GetProperties(

+    DWORD cbProperties,

+    __out_bcount(cbProperties) BYTE * pbProperties

+)

+{

+    if (0 != cbProperties) {

+        CheckPointer(pbProperties, E_POINTER);

+        //  Return generic stuff up to the length

+        AM_SAMPLE2_PROPERTIES Props;

+        Props.cbData     = min(cbProperties, sizeof(Props));

+        Props.dwSampleFlags = m_dwFlags & ~Sample_MediaTimeValid;

+        Props.dwTypeSpecificFlags = m_dwTypeSpecificFlags;

+        Props.pbBuffer   = m_pBuffer;

+        Props.cbBuffer   = m_cbBuffer;

+        Props.lActual    = m_lActual;

+        Props.tStart     = m_Start;

+        Props.tStop      = m_End;

+        Props.dwStreamId = m_dwStreamId;

+        if (m_dwFlags & AM_SAMPLE_TYPECHANGED) {

+            Props.pMediaType = m_pMediaType;

+        } else {

+            Props.pMediaType = NULL;

+        }

+        CopyMemory(pbProperties, &Props, Props.cbData);

+    }

+    return S_OK;

+}

+

+#define CONTAINS_FIELD(type, field, offset) \

+    ((FIELD_OFFSET(type, field) + sizeof(((type *)0)->field)) <= offset)

+

+HRESULT CMediaSample::SetProperties(

+    DWORD cbProperties,

+    __in_bcount(cbProperties) const BYTE * pbProperties

+)

+{

+

+    /*  Generic properties */

+    AM_MEDIA_TYPE *pMediaType = NULL;

+

+    if (CONTAINS_FIELD(AM_SAMPLE2_PROPERTIES, cbData, cbProperties)) {

+        CheckPointer(pbProperties, E_POINTER);

+        AM_SAMPLE2_PROPERTIES *pProps =

+            (AM_SAMPLE2_PROPERTIES *)pbProperties;

+

+        /*  Don't use more data than is actually there */

+        if (pProps->cbData < cbProperties) {

+            cbProperties = pProps->cbData;

+        }

+        /*  We only handle IMediaSample2 */

+        if (cbProperties > sizeof(*pProps) ||

+            pProps->cbData > sizeof(*pProps)) {

+            return E_INVALIDARG;

+        }

+        /*  Do checks first, the assignments (for backout) */

+        if (CONTAINS_FIELD(AM_SAMPLE2_PROPERTIES, dwSampleFlags, cbProperties)) {

+            /*  Check the flags */

+            if (pProps->dwSampleFlags &

+                    (~Sample_ValidFlags | Sample_MediaTimeValid)) {

+                return E_INVALIDARG;

+            }

+            /*  Check a flag isn't being set for a property

+                not being provided

+            */

+            if ((pProps->dwSampleFlags & AM_SAMPLE_TIMEVALID) &&

+                 !(m_dwFlags & AM_SAMPLE_TIMEVALID) &&

+                 !CONTAINS_FIELD(AM_SAMPLE2_PROPERTIES, tStop, cbProperties)) {

+                 return E_INVALIDARG;

+            }

+        }

+        /*  NB - can't SET the pointer or size */

+        if (CONTAINS_FIELD(AM_SAMPLE2_PROPERTIES, pbBuffer, cbProperties)) {

+

+            /*  Check pbBuffer */

+            if (pProps->pbBuffer != 0 && pProps->pbBuffer != m_pBuffer) {

+                return E_INVALIDARG;

+            }

+        }

+        if (CONTAINS_FIELD(AM_SAMPLE2_PROPERTIES, cbBuffer, cbProperties)) {

+

+            /*  Check cbBuffer */

+            if (pProps->cbBuffer != 0 && pProps->cbBuffer != m_cbBuffer) {

+                return E_INVALIDARG;

+            }

+        }

+        if (CONTAINS_FIELD(AM_SAMPLE2_PROPERTIES, cbBuffer, cbProperties) &&

+            CONTAINS_FIELD(AM_SAMPLE2_PROPERTIES, lActual, cbProperties)) {

+

+            /*  Check lActual */

+            if (pProps->cbBuffer < pProps->lActual) {

+                return E_INVALIDARG;

+            }

+        }

+

+        if (CONTAINS_FIELD(AM_SAMPLE2_PROPERTIES, pMediaType, cbProperties)) {

+

+            /*  Check pMediaType */

+            if (pProps->dwSampleFlags & AM_SAMPLE_TYPECHANGED) {

+                CheckPointer(pProps->pMediaType, E_POINTER);

+                pMediaType = CreateMediaType(pProps->pMediaType);

+                if (pMediaType == NULL) {

+                    return E_OUTOFMEMORY;

+                }

+            }

+        }

+

+        /*  Now do the assignments */

+        if (CONTAINS_FIELD(AM_SAMPLE2_PROPERTIES, dwStreamId, cbProperties)) {

+            m_dwStreamId = pProps->dwStreamId;

+        }

+        if (CONTAINS_FIELD(AM_SAMPLE2_PROPERTIES, dwSampleFlags, cbProperties)) {

+            /*  Set the flags */

+            m_dwFlags = pProps->dwSampleFlags |

+                                (m_dwFlags & Sample_MediaTimeValid);

+            m_dwTypeSpecificFlags = pProps->dwTypeSpecificFlags;

+        } else {

+            if (CONTAINS_FIELD(AM_SAMPLE2_PROPERTIES, dwTypeSpecificFlags, cbProperties)) {

+                m_dwTypeSpecificFlags = pProps->dwTypeSpecificFlags;

+            }

+        }

+

+        if (CONTAINS_FIELD(AM_SAMPLE2_PROPERTIES, lActual, cbProperties)) {

+            /*  Set lActual */

+            m_lActual = pProps->lActual;

+        }

+

+        if (CONTAINS_FIELD(AM_SAMPLE2_PROPERTIES, tStop, cbProperties)) {

+

+            /*  Set the times */

+            m_End   = pProps->tStop;

+        }

+        if (CONTAINS_FIELD(AM_SAMPLE2_PROPERTIES, tStart, cbProperties)) {

+

+            /*  Set the times */

+            m_Start = pProps->tStart;

+        }

+

+        if (CONTAINS_FIELD(AM_SAMPLE2_PROPERTIES, pMediaType, cbProperties)) {

+            /*  Set pMediaType */

+            if (pProps->dwSampleFlags & AM_SAMPLE_TYPECHANGED) {

+                if (m_pMediaType != NULL) {

+                    DeleteMediaType(m_pMediaType);

+                }

+                m_pMediaType = pMediaType;

+            }

+        }

+

+        /*  Fix up the type changed flag to correctly reflect the current state

+            If, for instance the input contained no type change but the

+            output does then if we don't do this we'd lose the

+            output media type.

+        */

+        if (m_pMediaType) {

+            m_dwFlags |= Sample_TypeChanged;

+        } else {

+            m_dwFlags &= ~Sample_TypeChanged;

+        }

+    }

+

+    return S_OK;

+}

+

+

+//

+// The streaming thread calls IPin::NewSegment(), IPin::EndOfStream(),

+// IMemInputPin::Receive() and IMemInputPin::ReceiveMultiple() on the

+// connected input pin.  The application thread calls Block().  The

+// following class members can only be called by the streaming thread.

+//

+//    Deliver()

+//    DeliverNewSegment()

+//    StartUsingOutputPin()

+//    StopUsingOutputPin()

+//    ChangeOutputFormat()

+//    ChangeMediaType()

+//    DynamicReconnect()

+//

+// The following class members can only be called by the application thread.

+//

+//    Block()

+//    SynchronousBlockOutputPin()

+//    AsynchronousBlockOutputPin()

+//

+

+CDynamicOutputPin::CDynamicOutputPin(

+    __in_opt LPCTSTR pObjectName,

+    __in CBaseFilter *pFilter,

+    __in CCritSec *pLock,

+    __inout HRESULT *phr,

+    __in_opt LPCWSTR pName) :

+        CBaseOutputPin(pObjectName, pFilter, pLock, phr, pName),

+        m_hStopEvent(NULL),

+        m_pGraphConfig(NULL),

+        m_bPinUsesReadOnlyAllocator(FALSE),

+        m_BlockState(NOT_BLOCKED),

+        m_hUnblockOutputPinEvent(NULL),

+        m_hNotifyCallerPinBlockedEvent(NULL),

+        m_dwBlockCallerThreadID(0),

+        m_dwNumOutstandingOutputPinUsers(0)

+{

+    HRESULT hr = Initialize();

+    if( FAILED( hr ) ) {

+        *phr = hr;

+        return;

+    }

+}

+

+#ifdef UNICODE

+CDynamicOutputPin::CDynamicOutputPin(

+    __in_opt LPCSTR pObjectName,

+    __in CBaseFilter *pFilter,

+    __in CCritSec *pLock,

+    __inout HRESULT *phr,

+    __in_opt LPCWSTR pName) :

+        CBaseOutputPin(pObjectName, pFilter, pLock, phr, pName),

+        m_hStopEvent(NULL),

+        m_pGraphConfig(NULL),

+        m_bPinUsesReadOnlyAllocator(FALSE),

+        m_BlockState(NOT_BLOCKED),

+        m_hUnblockOutputPinEvent(NULL),

+        m_hNotifyCallerPinBlockedEvent(NULL),

+        m_dwBlockCallerThreadID(0),

+        m_dwNumOutstandingOutputPinUsers(0)

+{

+    HRESULT hr = Initialize();

+    if( FAILED( hr ) ) {

+        *phr = hr;

+        return;

+    }

+}

+#endif

+

+CDynamicOutputPin::~CDynamicOutputPin()

+{

+    if(NULL != m_hUnblockOutputPinEvent) {

+        // This call should not fail because we have access to m_hUnblockOutputPinEvent

+        // and m_hUnblockOutputPinEvent is a valid event.

+        EXECUTE_ASSERT(::CloseHandle(m_hUnblockOutputPinEvent));

+    }

+

+    if(NULL != m_hNotifyCallerPinBlockedEvent) {

+        // This call should not fail because we have access to m_hNotifyCallerPinBlockedEvent

+        // and m_hNotifyCallerPinBlockedEvent is a valid event.

+        EXECUTE_ASSERT(::CloseHandle(m_hNotifyCallerPinBlockedEvent));

+    }

+}

+

+HRESULT CDynamicOutputPin::Initialize(void)

+{

+    m_hUnblockOutputPinEvent = ::CreateEvent( NULL,   // The event will have the default security descriptor.

+                                              TRUE,   // This is a manual reset event.

+                                              TRUE,   // The event is initially signaled.

+                                              NULL ); // The event is not named.

+

+    // CreateEvent() returns NULL if an error occurs.

+    if(NULL == m_hUnblockOutputPinEvent) {

+        return AmGetLastErrorToHResult();

+    }

+

+    //  Set flag to say we can reconnect while streaming.

+    SetReconnectWhenActive(true);

+

+    return S_OK;

+}

+

+STDMETHODIMP CDynamicOutputPin::NonDelegatingQueryInterface(REFIID riid, __deref_out void **ppv)

+{

+    if(riid == IID_IPinFlowControl) {

+        return GetInterface(static_cast<IPinFlowControl*>(this), ppv);

+    } else {

+        return CBaseOutputPin::NonDelegatingQueryInterface(riid, ppv);

+    }

+}

+

+STDMETHODIMP CDynamicOutputPin::Disconnect(void)

+{

+    CAutoLock cObjectLock(m_pLock);

+    return DisconnectInternal();

+}

+

+STDMETHODIMP CDynamicOutputPin::Block(DWORD dwBlockFlags, HANDLE hEvent)

+{

+    const DWORD VALID_FLAGS = AM_PIN_FLOW_CONTROL_BLOCK;

+

+    // Check for illegal flags.

+    if(dwBlockFlags & ~VALID_FLAGS) {

+        return E_INVALIDARG;

+    }

+

+    // Make sure the event is unsignaled.

+    if((dwBlockFlags & AM_PIN_FLOW_CONTROL_BLOCK) && (NULL != hEvent)) {

+        if( !::ResetEvent( hEvent ) ) {

+            return AmGetLastErrorToHResult();

+        }

+    }

+

+    // No flags are set if we are unblocking the output pin.

+    if(0 == dwBlockFlags) {

+

+        // This parameter should be NULL because unblock operations are always synchronous.

+        // There is no need to notify the caller when the event is done.

+        if(NULL != hEvent) {

+            return E_INVALIDARG;

+        }

+    }

+

+    #ifdef DEBUG

+    AssertValid();

+    #endif // DEBUG

+

+    HRESULT hr;

+

+    if(dwBlockFlags & AM_PIN_FLOW_CONTROL_BLOCK) {

+        // IPinFlowControl::Block()'s hEvent parameter is NULL if the block is synchronous.

+        // If hEvent is not NULL, the block is asynchronous.

+        if(NULL == hEvent) {

+            hr = SynchronousBlockOutputPin();

+        } else {

+            hr = AsynchronousBlockOutputPin(hEvent);

+        }

+    } else {

+        hr = UnblockOutputPin();

+    }

+

+    #ifdef DEBUG

+    AssertValid();

+    #endif // DEBUG

+

+    if(FAILED(hr)) {

+        return hr;

+    }

+

+    return S_OK;

+}

+

+HRESULT CDynamicOutputPin::SynchronousBlockOutputPin(void)

+{

+    HANDLE hNotifyCallerPinBlockedEvent = :: CreateEvent( NULL,   // The event will have the default security attributes.

+                                                          FALSE,  // This is an automatic reset event.

+                                                          FALSE,  // The event is initially unsignaled.

+                                                          NULL ); // The event is not named.

+

+    // CreateEvent() returns NULL if an error occurs.

+    if(NULL == hNotifyCallerPinBlockedEvent) {

+        return AmGetLastErrorToHResult();

+    }

+

+    HRESULT hr = AsynchronousBlockOutputPin(hNotifyCallerPinBlockedEvent);

+    if(FAILED(hr)) {

+        // This call should not fail because we have access to hNotifyCallerPinBlockedEvent

+        // and hNotifyCallerPinBlockedEvent is a valid event.

+        EXECUTE_ASSERT(::CloseHandle(hNotifyCallerPinBlockedEvent));

+

+        return hr;

+    }

+

+    hr = WaitEvent(hNotifyCallerPinBlockedEvent);

+

+    // This call should not fail because we have access to hNotifyCallerPinBlockedEvent

+    // and hNotifyCallerPinBlockedEvent is a valid event.

+    EXECUTE_ASSERT(::CloseHandle(hNotifyCallerPinBlockedEvent));

+

+    if(FAILED(hr)) {

+        return hr;

+    }

+

+    return S_OK;

+}

+

+HRESULT CDynamicOutputPin::AsynchronousBlockOutputPin(HANDLE hNotifyCallerPinBlockedEvent)

+{

+    // This function holds the m_BlockStateLock because it uses

+    // m_dwBlockCallerThreadID, m_BlockState and

+    // m_hNotifyCallerPinBlockedEvent.

+    CAutoLock alBlockStateLock(&m_BlockStateLock);

+

+    if(NOT_BLOCKED != m_BlockState) {

+        if(m_dwBlockCallerThreadID == ::GetCurrentThreadId()) {

+            return VFW_E_PIN_ALREADY_BLOCKED_ON_THIS_THREAD;

+        } else {

+            return VFW_E_PIN_ALREADY_BLOCKED;

+        }

+    }

+

+    BOOL fSuccess = ::DuplicateHandle( ::GetCurrentProcess(),

+                                       hNotifyCallerPinBlockedEvent,

+                                       ::GetCurrentProcess(),

+                                       &m_hNotifyCallerPinBlockedEvent,

+                                       EVENT_MODIFY_STATE,

+                                       FALSE,

+                                       0 );

+    if( !fSuccess ) {

+        return AmGetLastErrorToHResult();

+    }

+

+    m_BlockState = PENDING;

+    m_dwBlockCallerThreadID = ::GetCurrentThreadId();

+

+    // The output pin cannot be blocked if the streaming thread is

+    // calling IPin::NewSegment(), IPin::EndOfStream(), IMemInputPin::Receive()

+    // or IMemInputPin::ReceiveMultiple() on the connected input pin.  Also, it

+    // cannot be blocked if the streaming thread is calling DynamicReconnect(),

+    // ChangeMediaType() or ChangeOutputFormat().

+    if(!StreamingThreadUsingOutputPin()) {

+

+        // The output pin can be immediately blocked.

+        BlockOutputPin();

+    }

+

+    return S_OK;

+}

+

+void CDynamicOutputPin::BlockOutputPin(void)

+{

+    // The caller should always hold the m_BlockStateLock because this function

+    // uses m_BlockState and m_hNotifyCallerPinBlockedEvent.

+    ASSERT(CritCheckIn(&m_BlockStateLock));

+

+    // This function should not be called if the streaming thread is modifying

+    // the connection state or it's passing data downstream.

+    ASSERT(!StreamingThreadUsingOutputPin());

+

+    // This should not fail because we successfully created the event

+    // and we have the security permissions to change it's state.

+    EXECUTE_ASSERT(::ResetEvent(m_hUnblockOutputPinEvent));

+

+    // This event should not fail because AsynchronousBlockOutputPin() successfully

+    // duplicated this handle and we have the appropriate security permissions.

+    EXECUTE_ASSERT(::SetEvent(m_hNotifyCallerPinBlockedEvent));

+    EXECUTE_ASSERT(::CloseHandle(m_hNotifyCallerPinBlockedEvent));

+

+    m_BlockState = BLOCKED;

+    m_hNotifyCallerPinBlockedEvent = NULL;

+}

+

+HRESULT CDynamicOutputPin::UnblockOutputPin(void)

+{

+    // UnblockOutputPin() holds the m_BlockStateLock because it

+    // uses m_BlockState, m_dwBlockCallerThreadID and

+    // m_hNotifyCallerPinBlockedEvent.

+    CAutoLock alBlockStateLock(&m_BlockStateLock);

+

+    if(NOT_BLOCKED == m_BlockState) {

+        return S_FALSE;

+    }

+

+    // This should not fail because we successfully created the event

+    // and we have the security permissions to change it's state.

+    EXECUTE_ASSERT(::SetEvent(m_hUnblockOutputPinEvent));

+

+    // Cancel the block operation if it's still pending.

+    if(NULL != m_hNotifyCallerPinBlockedEvent) {

+        // This event should not fail because AsynchronousBlockOutputPin() successfully

+        // duplicated this handle and we have the appropriate security permissions.

+        EXECUTE_ASSERT(::SetEvent(m_hNotifyCallerPinBlockedEvent));

+        EXECUTE_ASSERT(::CloseHandle(m_hNotifyCallerPinBlockedEvent));

+    }

+

+    m_BlockState = NOT_BLOCKED;

+    m_dwBlockCallerThreadID = 0;

+    m_hNotifyCallerPinBlockedEvent = NULL;

+

+    return S_OK;

+}

+

+HRESULT CDynamicOutputPin::StartUsingOutputPin(void)

+{

+    // The caller should not hold m_BlockStateLock.  If the caller does,

+    // a deadlock could occur.

+    ASSERT(CritCheckOut(&m_BlockStateLock));

+

+    CAutoLock alBlockStateLock(&m_BlockStateLock);

+

+    #ifdef DEBUG

+    AssertValid();

+    #endif // DEBUG

+

+    // Are we in the middle of a block operation?

+    while(BLOCKED == m_BlockState) {

+        m_BlockStateLock.Unlock();

+

+        // If this ASSERT fires, a deadlock could occur.  The caller should make sure

+        // that this thread never acquires the Block State lock more than once.

+        ASSERT(CritCheckOut( &m_BlockStateLock ));

+

+        // WaitForMultipleObjects() returns WAIT_OBJECT_0 if the unblock event

+        // is fired.  It returns WAIT_OBJECT_0 + 1 if the stop event if fired.

+        // See the Windows SDK documentation for more information on

+        // WaitForMultipleObjects().

+        const DWORD UNBLOCK = WAIT_OBJECT_0;

+        const DWORD STOP = WAIT_OBJECT_0 + 1;

+

+        HANDLE ahWaitEvents[] = { m_hUnblockOutputPinEvent, m_hStopEvent };

+        DWORD dwNumWaitEvents = sizeof(ahWaitEvents)/sizeof(HANDLE);

+

+        DWORD dwReturnValue = ::WaitForMultipleObjects( dwNumWaitEvents, ahWaitEvents, FALSE, INFINITE );

+

+        m_BlockStateLock.Lock();

+

+        #ifdef DEBUG

+        AssertValid();

+        #endif // DEBUG

+

+        switch( dwReturnValue ) {

+        case UNBLOCK:

+            break;

+

+        case STOP:

+            return VFW_E_STATE_CHANGED;

+

+        case WAIT_FAILED:

+            return AmGetLastErrorToHResult();

+

+        default:

+            DbgBreak( "An Unexpected case occured in CDynamicOutputPin::StartUsingOutputPin()." );

+            return E_UNEXPECTED;

+        }

+    }

+

+    m_dwNumOutstandingOutputPinUsers++;

+

+    #ifdef DEBUG

+    AssertValid();

+    #endif // DEBUG

+

+    return S_OK;

+}

+

+void CDynamicOutputPin::StopUsingOutputPin(void)

+{

+    CAutoLock alBlockStateLock(&m_BlockStateLock);

+

+    #ifdef DEBUG

+    AssertValid();

+    #endif // DEBUG

+

+    m_dwNumOutstandingOutputPinUsers--;

+

+    if((m_dwNumOutstandingOutputPinUsers == 0) && (NOT_BLOCKED != m_BlockState)) {

+        BlockOutputPin();

+    }

+

+    #ifdef DEBUG

+    AssertValid();

+    #endif // DEBUG

+}

+

+bool CDynamicOutputPin::StreamingThreadUsingOutputPin(void)

+{

+    CAutoLock alBlockStateLock(&m_BlockStateLock);

+

+    return (m_dwNumOutstandingOutputPinUsers > 0);

+}

+

+void CDynamicOutputPin::SetConfigInfo(IGraphConfig *pGraphConfig, HANDLE hStopEvent)

+{

+    // This pointer is not addrefed because filters are not allowed to

+    // hold references to the filter graph manager.  See the documentation for

+    // IBaseFilter::JoinFilterGraph() in the Direct Show SDK for more information.

+    m_pGraphConfig = pGraphConfig;

+

+    m_hStopEvent = hStopEvent;

+}

+

+HRESULT CDynamicOutputPin::Active(void)

+{

+    // Make sure the user initialized the object by calling SetConfigInfo().

+    if((NULL == m_hStopEvent) || (NULL == m_pGraphConfig)) {

+        DbgBreak( ERROR: CDynamicOutputPin::Active() failed because m_pGraphConfig and m_hStopEvent were not initialized.  Call SetConfigInfo() to initialize them. );

+        return E_FAIL;

+    }

+

+    // If this ASSERT fires, the user may have passed an invalid event handle to SetConfigInfo().

+    // The ASSERT can also fire if the event if destroyed and then Active() is called.  An event

+    // handle is invalid if 1) the event does not exist or the user does not have the security

+    // permissions to use the event.

+    EXECUTE_ASSERT(ResetEvent(m_hStopEvent));

+

+    return CBaseOutputPin::Active();

+}

+

+HRESULT CDynamicOutputPin::Inactive(void)

+{

+    // If this ASSERT fires, the user may have passed an invalid event handle to SetConfigInfo().

+    // The ASSERT can also fire if the event if destroyed and then Active() is called.  An event

+    // handle is invalid if 1) the event does not exist or the user does not have the security

+    // permissions to use the event.

+    EXECUTE_ASSERT(SetEvent(m_hStopEvent));

+

+    return CBaseOutputPin::Inactive();

+}

+

+HRESULT CDynamicOutputPin::DeliverBeginFlush(void)

+{

+    // If this ASSERT fires, the user may have passed an invalid event handle to SetConfigInfo().

+    // The ASSERT can also fire if the event if destroyed and then DeliverBeginFlush() is called.

+    // An event handle is invalid if 1) the event does not exist or the user does not have the security

+    // permissions to use the event.

+    EXECUTE_ASSERT(SetEvent(m_hStopEvent));

+

+    return CBaseOutputPin::DeliverBeginFlush();

+}

+

+HRESULT CDynamicOutputPin::DeliverEndFlush(void)

+{

+    // If this ASSERT fires, the user may have passed an invalid event handle to SetConfigInfo().

+    // The ASSERT can also fire if the event if destroyed and then DeliverBeginFlush() is called.

+    // An event handle is invalid if 1) the event does not exist or the user does not have the security

+    // permissions to use the event.

+    EXECUTE_ASSERT(ResetEvent(m_hStopEvent));

+

+    return CBaseOutputPin::DeliverEndFlush();

+}

+

+

+// ChangeOutputFormat() either dynamicly changes the connection's format type or it dynamicly

+// reconnects the output pin.

+HRESULT CDynamicOutputPin::ChangeOutputFormat

+    (

+    const AM_MEDIA_TYPE *pmt,

+    REFERENCE_TIME tSegmentStart,

+    REFERENCE_TIME tSegmentStop,

+    double dSegmentRate

+    )

+{

+    // The caller should call StartUsingOutputPin() before calling this

+    // method.

+    ASSERT(StreamingThreadUsingOutputPin());

+

+    // Callers should always pass a valid media type to ChangeOutputFormat() .

+    ASSERT(NULL != pmt);

+

+    CMediaType cmt(*pmt);

+    HRESULT hr = ChangeMediaType(&cmt);

+    if (FAILED(hr)) {

+        return hr;

+    }

+

+    hr = DeliverNewSegment(tSegmentStart, tSegmentStop, dSegmentRate);

+    if( FAILED( hr ) ) {

+        return hr;

+    }

+

+    return S_OK;

+}

+

+HRESULT CDynamicOutputPin::ChangeMediaType(const CMediaType *pmt)

+{

+    // The caller should call StartUsingOutputPin() before calling this

+    // method.

+    ASSERT(StreamingThreadUsingOutputPin());

+

+    // This function assumes the filter graph is running.

+    ASSERT(!IsStopped());

+

+    if(!IsConnected()) {

+        return VFW_E_NOT_CONNECTED;

+    }

+

+    /*  First check if the downstream pin will accept a dynamic

+        format change

+    */

+    QzCComPtr<IPinConnection> pConnection;

+

+    m_Connected->QueryInterface(IID_IPinConnection, (void **)&pConnection);

+    if(pConnection != NULL) {

+

+        if(S_OK == pConnection->DynamicQueryAccept(pmt)) {

+

+            HRESULT hr = ChangeMediaTypeHelper(pmt);

+            if(FAILED(hr)) {

+                return hr;

+            }

+

+            return S_OK;

+        }

+    }

+

+    /*  Can't do the dynamic connection */

+    return DynamicReconnect(pmt);

+}

+

+HRESULT CDynamicOutputPin::ChangeMediaTypeHelper(const CMediaType *pmt)

+{

+    // The caller should call StartUsingOutputPin() before calling this

+    // method.

+    ASSERT(StreamingThreadUsingOutputPin());

+

+    HRESULT hr = m_Connected->ReceiveConnection(this, pmt);

+    if(FAILED(hr)) {

+        return hr;

+    }

+

+    hr = SetMediaType(pmt);

+    if(FAILED(hr)) {

+        return hr;

+    }

+

+    // Does this pin use the local memory transport?

+    if(NULL != m_pInputPin) {

+        // This function assumes that m_pInputPin and m_Connected are

+        // two different interfaces to the same object.

+        ASSERT(::IsEqualObject(m_Connected, m_pInputPin));

+

+        ALLOCATOR_PROPERTIES apInputPinRequirements;

+        apInputPinRequirements.cbAlign = 0;

+        apInputPinRequirements.cbBuffer = 0;

+        apInputPinRequirements.cbPrefix = 0;

+        apInputPinRequirements.cBuffers = 0;

+

+        m_pInputPin->GetAllocatorRequirements(&apInputPinRequirements);

+

+        // A zero allignment does not make any sense.

+        if(0 == apInputPinRequirements.cbAlign) {

+            apInputPinRequirements.cbAlign = 1;

+        }

+

+        hr = m_pAllocator->Decommit();

+        if(FAILED(hr)) {

+            return hr;

+        }

+

+        hr = DecideBufferSize(m_pAllocator,  &apInputPinRequirements);

+        if(FAILED(hr)) {

+            return hr;

+        }

+

+        hr = m_pAllocator->Commit();

+        if(FAILED(hr)) {

+            return hr;

+        }

+

+        hr = m_pInputPin->NotifyAllocator(m_pAllocator, m_bPinUsesReadOnlyAllocator);

+        if(FAILED(hr)) {

+            return hr;

+        }

+    }

+

+    return S_OK;

+}

+

+// this method has to be called from the thread that is pushing data,

+// and it's the caller's responsibility to make sure that the thread

+// has no outstand samples because they cannot be delivered after a

+// reconnect

+//

+HRESULT CDynamicOutputPin::DynamicReconnect( const CMediaType* pmt )

+{

+    // The caller should call StartUsingOutputPin() before calling this

+    // method.

+    ASSERT(StreamingThreadUsingOutputPin());

+

+    if((m_pGraphConfig == NULL) || (NULL == m_hStopEvent)) {

+        return E_FAIL;

+    }

+

+    HRESULT hr = m_pGraphConfig->Reconnect(

+        this,

+        NULL,

+        pmt,

+        NULL,

+        m_hStopEvent,

+        AM_GRAPH_CONFIG_RECONNECT_CACHE_REMOVED_FILTERS );

+

+    return hr;

+}

+

+HRESULT CDynamicOutputPin::CompleteConnect(IPin *pReceivePin)

+{

+    HRESULT hr = CBaseOutputPin::CompleteConnect(pReceivePin);

+    if(SUCCEEDED(hr)) {

+        if(!IsStopped() && m_pAllocator) {

+            hr = m_pAllocator->Commit();

+            ASSERT(hr != VFW_E_ALREADY_COMMITTED);

+        }

+    }

+

+    return hr;

+}

+

+#ifdef DEBUG

+void CDynamicOutputPin::AssertValid(void)

+{

+    // Make sure the object was correctly initialized.

+

+    // This ASSERT only fires if the object failed to initialize

+    // and the user ignored the constructor's return code (phr).

+    ASSERT(NULL != m_hUnblockOutputPinEvent);

+

+    // If either of these ASSERTs fire, the user did not correctly call

+    // SetConfigInfo().

+    ASSERT(NULL != m_hStopEvent);

+    ASSERT(NULL != m_pGraphConfig);

+

+    // Make sure the block state is consistent.

+

+    CAutoLock alBlockStateLock(&m_BlockStateLock);

+

+    // BLOCK_STATE variables only have three legal values: PENDING, BLOCKED and NOT_BLOCKED.

+    ASSERT((NOT_BLOCKED == m_BlockState) || (PENDING == m_BlockState) || (BLOCKED == m_BlockState));

+

+    // m_hNotifyCallerPinBlockedEvent is only needed when a block operation cannot complete

+    // immediately.

+    ASSERT(((NULL == m_hNotifyCallerPinBlockedEvent) && (PENDING != m_BlockState)) ||

+           ((NULL != m_hNotifyCallerPinBlockedEvent) && (PENDING == m_BlockState)) );

+

+    // m_dwBlockCallerThreadID should always be 0 if the pin is not blocked and

+    // the user is not trying to block the pin.

+    ASSERT((0 == m_dwBlockCallerThreadID) || (NOT_BLOCKED != m_BlockState));

+

+    // If this ASSERT fires, the streaming thread is using the output pin and the

+    // output pin is blocked.

+    ASSERT(((0 != m_dwNumOutstandingOutputPinUsers) && (BLOCKED != m_BlockState)) ||

+           ((0 == m_dwNumOutstandingOutputPinUsers) && (NOT_BLOCKED != m_BlockState)) ||

+           ((0 == m_dwNumOutstandingOutputPinUsers) && (NOT_BLOCKED == m_BlockState)) );

+}

+#endif // DEBUG

+

+HRESULT CDynamicOutputPin::WaitEvent(HANDLE hEvent)

+{

+    const DWORD EVENT_SIGNALED = WAIT_OBJECT_0;

+

+    DWORD dwReturnValue = ::WaitForSingleObject(hEvent, INFINITE);

+

+    switch( dwReturnValue ) {

+    case EVENT_SIGNALED:

+        return S_OK;

+

+    case WAIT_FAILED:

+        return AmGetLastErrorToHResult();

+

+    default:

+        DbgBreak( "An Unexpected case occured in CDynamicOutputPin::WaitEvent()." );

+        return E_UNEXPECTED;

+    }

+}

+

+//=====================================================================

+//=====================================================================

+// Implements CBaseAllocator

+//=====================================================================

+//=====================================================================

+

+

+/* Constructor overrides the default settings for the free list to request

+   that it be alertable (ie the list can be cast to a handle which can be

+   passed to WaitForSingleObject). Both of the allocator lists also ask for

+   object locking, the all list matches the object default settings but I

+   have included them here just so it is obvious what kind of list it is */

+

+CBaseAllocator::CBaseAllocator(__in_opt LPCTSTR pName,

+                               __inout_opt LPUNKNOWN pUnk,

+                               __inout HRESULT *phr,

+                               BOOL bEvent,

+                               BOOL fEnableReleaseCallback

+                               ) :

+    CUnknown(pName, pUnk),

+    m_lAllocated(0),

+    m_bChanged(FALSE),

+    m_bCommitted(FALSE),

+    m_bDecommitInProgress(FALSE),

+    m_lSize(0),

+    m_lCount(0),

+    m_lAlignment(0),

+    m_lPrefix(0),

+    m_hSem(NULL),

+    m_lWaiting(0),

+    m_fEnableReleaseCallback(fEnableReleaseCallback),

+    m_pNotify(NULL)

+{

+#ifdef DXMPERF

+    PERFLOG_CTOR( pName ? pName : L"CBaseAllocator", (IMemAllocator *) this );

+#endif // DXMPERF

+

+    if (bEvent) {

+        m_hSem = CreateSemaphore(NULL, 0, 0x7FFFFFFF, NULL);

+        if (m_hSem == NULL) {

+            *phr = E_OUTOFMEMORY;

+            return;

+        }

+    }

+}

+

+#ifdef UNICODE

+CBaseAllocator::CBaseAllocator(__in_opt LPCSTR pName,

+                               __inout_opt LPUNKNOWN pUnk,

+                               __inout HRESULT *phr,

+                               BOOL bEvent,

+                               BOOL fEnableReleaseCallback) :

+    CUnknown(pName, pUnk),

+    m_lAllocated(0),

+    m_bChanged(FALSE),

+    m_bCommitted(FALSE),

+    m_bDecommitInProgress(FALSE),

+    m_lSize(0),

+    m_lCount(0),

+    m_lAlignment(0),

+    m_lPrefix(0),

+    m_hSem(NULL),

+    m_lWaiting(0),

+    m_fEnableReleaseCallback(fEnableReleaseCallback),

+    m_pNotify(NULL)

+{

+#ifdef DXMPERF

+    PERFLOG_CTOR( L"CBaseAllocator", (IMemAllocator *) this );

+#endif // DXMPERF

+

+    if (bEvent) {

+        m_hSem = CreateSemaphore(NULL, 0, 0x7FFFFFFF, NULL);

+        if (m_hSem == NULL) {

+            *phr = E_OUTOFMEMORY;

+            return;

+        }

+    }

+}

+#endif

+

+/* Destructor */

+

+CBaseAllocator::~CBaseAllocator()

+{

+    // we can't call Decommit here since that would mean a call to a

+    // pure virtual in destructor.

+    // We must assume that the derived class has gone into decommit state in

+    // its destructor.

+#ifdef DXMPERF

+    PERFLOG_DTOR( L"CBaseAllocator", (IMemAllocator *) this );

+#endif // DXMPERF

+

+    ASSERT(!m_bCommitted);

+    if (m_hSem != NULL) {

+        EXECUTE_ASSERT(CloseHandle(m_hSem));

+    }

+    if (m_pNotify) {

+        m_pNotify->Release();

+    }

+}

+

+

+/* Override this to publicise our interfaces */

+

+STDMETHODIMP

+CBaseAllocator::NonDelegatingQueryInterface(REFIID riid, __deref_out void **ppv)

+{

+    /* Do we know about this interface */

+

+    if (riid == IID_IMemAllocator ||

+        riid == IID_IMemAllocatorCallbackTemp && m_fEnableReleaseCallback) {

+        return GetInterface((IMemAllocatorCallbackTemp *) this, ppv);

+    } else {

+        return CUnknown::NonDelegatingQueryInterface(riid, ppv);

+    }

+}

+

+

+/* This sets the size and count of the required samples. The memory isn't

+   actually allocated until Commit() is called, if memory has already been

+   allocated then assuming no samples are outstanding the user may call us

+   to change the buffering, the memory will be released in Commit() */

+

+STDMETHODIMP

+CBaseAllocator::SetProperties(

+                __in ALLOCATOR_PROPERTIES* pRequest,

+                __out ALLOCATOR_PROPERTIES* pActual)

+{

+    CheckPointer(pRequest, E_POINTER);

+    CheckPointer(pActual, E_POINTER);

+    ValidateReadWritePtr(pActual, sizeof(ALLOCATOR_PROPERTIES));

+    CAutoLock cObjectLock(this);

+

+    ZeroMemory(pActual, sizeof(ALLOCATOR_PROPERTIES));

+

+    ASSERT(pRequest->cbBuffer > 0);

+

+    /*  Check the alignment requested */

+    if (pRequest->cbAlign != 1) {

+        DbgLog((LOG_ERROR, 2, TEXT("Alignment requested was 0x%x, not 1"),

+               pRequest->cbAlign));

+        return VFW_E_BADALIGN;

+    }

+

+    /* Can't do this if already committed, there is an argument that says we

+       should not reject the SetProperties call if there are buffers still

+       active. However this is called by the source filter, which is the same

+       person who is holding the samples. Therefore it is not unreasonable

+       for them to free all their samples before changing the requirements */

+

+    if (m_bCommitted) {

+        return VFW_E_ALREADY_COMMITTED;

+    }

+

+    /* Must be no outstanding buffers */

+

+    if (m_lAllocated != m_lFree.GetCount()) {

+        return VFW_E_BUFFERS_OUTSTANDING;

+    }

+

+    /* There isn't any real need to check the parameters as they

+       will just be rejected when the user finally calls Commit */

+

+    pActual->cbBuffer = m_lSize = pRequest->cbBuffer;

+    pActual->cBuffers = m_lCount = pRequest->cBuffers;

+    pActual->cbAlign = m_lAlignment = pRequest->cbAlign;

+    pActual->cbPrefix = m_lPrefix = pRequest->cbPrefix;

+

+    m_bChanged = TRUE;

+    return NOERROR;

+}

+

+STDMETHODIMP

+CBaseAllocator::GetProperties(

+    __out ALLOCATOR_PROPERTIES * pActual)

+{

+    CheckPointer(pActual,E_POINTER);

+    ValidateReadWritePtr(pActual,sizeof(ALLOCATOR_PROPERTIES));

+

+    CAutoLock cObjectLock(this);

+    pActual->cbBuffer = m_lSize;

+    pActual->cBuffers = m_lCount;

+    pActual->cbAlign = m_lAlignment;

+    pActual->cbPrefix = m_lPrefix;

+    return NOERROR;

+}

+

+// get container for a sample. Blocking, synchronous call to get the

+// next free buffer (as represented by an IMediaSample interface).

+// on return, the time etc properties will be invalid, but the buffer

+// pointer and size will be correct.

+

+HRESULT CBaseAllocator::GetBuffer(__deref_out IMediaSample **ppBuffer,

+                                  __in_opt REFERENCE_TIME *pStartTime,

+                                  __in_opt REFERENCE_TIME *pEndTime,

+                                  DWORD dwFlags

+                                  )

+{

+    UNREFERENCED_PARAMETER(pStartTime);

+    UNREFERENCED_PARAMETER(pEndTime);

+    UNREFERENCED_PARAMETER(dwFlags);

+    CMediaSample *pSample;

+

+    *ppBuffer = NULL;

+    for (;;)

+    {

+        {  // scope for lock

+            CAutoLock cObjectLock(this);

+

+            /* Check we are committed */

+            if (!m_bCommitted) {

+                return VFW_E_NOT_COMMITTED;

+            }

+            pSample = (CMediaSample *) m_lFree.RemoveHead();

+            if (pSample == NULL) {

+                SetWaiting();

+            }

+        }

+

+        /* If we didn't get a sample then wait for the list to signal */

+

+        if (pSample) {

+            break;

+        }

+        if (dwFlags & AM_GBF_NOWAIT) {

+            return VFW_E_TIMEOUT;

+        }

+        ASSERT(m_hSem != NULL);

+        WaitForSingleObject(m_hSem, INFINITE);

+    }

+

+    /* Addref the buffer up to one. On release

+       back to zero instead of being deleted, it will requeue itself by

+       calling the ReleaseBuffer member function. NOTE the owner of a

+       media sample must always be derived from CBaseAllocator */

+

+

+    ASSERT(pSample->m_cRef == 0);

+    pSample->m_cRef = 1;

+    *ppBuffer = pSample;

+

+#ifdef DXMPERF

+    PERFLOG_GETBUFFER( (IMemAllocator *) this, pSample );

+#endif // DXMPERF

+

+    return NOERROR;

+}

+

+

+/* Final release of a CMediaSample will call this */

+

+STDMETHODIMP

+CBaseAllocator::ReleaseBuffer(IMediaSample * pSample)

+{

+    CheckPointer(pSample,E_POINTER);

+    ValidateReadPtr(pSample,sizeof(IMediaSample));

+

+#ifdef DXMPERF

+    PERFLOG_RELBUFFER( (IMemAllocator *) this, pSample );

+#endif // DXMPERF

+

+

+    BOOL bRelease = FALSE;

+    {

+        CAutoLock cal(this);

+

+        /* Put back on the free list */

+

+        m_lFree.Add((CMediaSample *)pSample);

+        if (m_lWaiting != 0) {

+            NotifySample();

+        }

+

+        // if there is a pending Decommit, then we need to complete it by

+        // calling Free() when the last buffer is placed on the free list

+

+        LONG l1 = m_lFree.GetCount();

+        if (m_bDecommitInProgress && (l1 == m_lAllocated)) {

+            Free();

+            m_bDecommitInProgress = FALSE;

+            bRelease = TRUE;

+        }

+    }

+

+    if (m_pNotify) {

+

+        ASSERT(m_fEnableReleaseCallback);

+

+        //

+        // Note that this is not synchronized with setting up a notification

+        // method.

+        //

+        m_pNotify->NotifyRelease();

+    }

+

+    /* For each buffer there is one AddRef, made in GetBuffer and released

+       here. This may cause the allocator and all samples to be deleted */

+

+    if (bRelease) {

+        Release();

+    }

+    return NOERROR;

+}

+

+STDMETHODIMP

+CBaseAllocator::SetNotify(

+    IMemAllocatorNotifyCallbackTemp* pNotify

+    )

+{

+    ASSERT(m_fEnableReleaseCallback);

+    CAutoLock lck(this);

+    if (pNotify) {

+        pNotify->AddRef();

+    }

+    if (m_pNotify) {

+        m_pNotify->Release();

+    }

+    m_pNotify = pNotify;

+    return S_OK;

+}

+

+STDMETHODIMP

+CBaseAllocator::GetFreeCount(

+    __out LONG* plBuffersFree

+    )

+{

+    ASSERT(m_fEnableReleaseCallback);

+    CAutoLock cObjectLock(this);

+    *plBuffersFree = m_lCount - m_lAllocated + m_lFree.GetCount();

+    return NOERROR;

+}

+

+void

+CBaseAllocator::NotifySample()

+{

+    if (m_lWaiting != 0) {

+        ASSERT(m_hSem != NULL);

+        ReleaseSemaphore(m_hSem, m_lWaiting, 0);

+        m_lWaiting = 0;

+    }

+}

+

+STDMETHODIMP

+CBaseAllocator::Commit()

+{

+    /* Check we are not decommitted */

+    CAutoLock cObjectLock(this);

+

+    // cannot need to alloc or re-alloc if we are committed

+    if (m_bCommitted) {

+        return NOERROR;

+    }

+

+    /* Allow GetBuffer calls */

+

+    m_bCommitted = TRUE;

+

+    // is there a pending decommit ? if so, just cancel it

+    if (m_bDecommitInProgress) {

+        m_bDecommitInProgress = FALSE;

+

+        // don't call Alloc at this point. He cannot allow SetProperties

+        // between Decommit and the last free, so the buffer size cannot have

+        // changed. And because some of the buffers are not free yet, he

+        // cannot re-alloc anyway.

+        return NOERROR;

+    }

+

+    DbgLog((LOG_MEMORY, 1, TEXT("Allocating: %ldx%ld"), m_lCount, m_lSize));

+

+    // actually need to allocate the samples

+    HRESULT hr = Alloc();

+    if (FAILED(hr)) {

+        m_bCommitted = FALSE;

+        return hr;

+    }

+    AddRef();

+    return NOERROR;

+}

+

+

+STDMETHODIMP

+CBaseAllocator::Decommit()

+{

+    BOOL bRelease = FALSE;

+    {

+        /* Check we are not already decommitted */

+        CAutoLock cObjectLock(this);

+        if (m_bCommitted == FALSE) {

+            if (m_bDecommitInProgress == FALSE) {

+                return NOERROR;

+            }

+        }

+

+        /* No more GetBuffer calls will succeed */

+        m_bCommitted = FALSE;

+

+        // are any buffers outstanding?

+        if (m_lFree.GetCount() < m_lAllocated) {

+            // please complete the decommit when last buffer is freed

+            m_bDecommitInProgress = TRUE;

+        } else {

+            m_bDecommitInProgress = FALSE;

+

+            // need to complete the decommit here as there are no

+            // outstanding buffers

+

+            Free();

+            bRelease = TRUE;

+        }

+

+        // Tell anyone waiting that they can go now so we can

+        // reject their call

+#pragma warning(push)

+#ifndef _PREFAST_

+#pragma warning(disable:4068)

+#endif

+#pragma prefast(suppress:__WARNING_DEREF_NULL_PTR, "Suppress warning related to Free() invalidating 'this' which is no applicable to CBaseAllocator::Free()")

+        NotifySample();

+

+#pragma warning(pop)

+    }

+

+    if (bRelease) {

+        Release();

+    }

+    return NOERROR;

+}

+

+

+/* Base definition of allocation which checks we are ok to go ahead and do

+   the full allocation. We return S_FALSE if the requirements are the same */

+

+HRESULT

+CBaseAllocator::Alloc(void)

+{

+    /* Error if he hasn't set the size yet */

+    if (m_lCount <= 0 || m_lSize <= 0 || m_lAlignment <= 0) {

+        return VFW_E_SIZENOTSET;

+    }

+

+    /* should never get here while buffers outstanding */

+    ASSERT(m_lFree.GetCount() == m_lAllocated);

+

+    /* If the requirements haven't changed then don't reallocate */

+    if (m_bChanged == FALSE) {

+        return S_FALSE;

+    }

+

+    return NOERROR;

+}

+

+/*  Implement CBaseAllocator::CSampleList::Remove(pSample)

+    Removes pSample from the list

+*/

+void

+CBaseAllocator::CSampleList::Remove(__inout CMediaSample * pSample)

+{

+    CMediaSample **pSearch;

+    for (pSearch = &m_List;

+         *pSearch != NULL;

+         pSearch = &(CBaseAllocator::NextSample(*pSearch))) {

+       if (*pSearch == pSample) {

+           *pSearch = CBaseAllocator::NextSample(pSample);

+           CBaseAllocator::NextSample(pSample) = NULL;

+           m_nOnList--;

+           return;

+       }

+    }

+    DbgBreak("Couldn't find sample in list");

+}

+

+//=====================================================================

+//=====================================================================

+// Implements CMemAllocator

+//=====================================================================

+//=====================================================================

+

+

+/* This goes in the factory template table to create new instances */

+CUnknown *CMemAllocator::CreateInstance(__inout_opt LPUNKNOWN pUnk, __inout HRESULT *phr)

+{

+    CUnknown *pUnkRet = new CMemAllocator(NAME("CMemAllocator"), pUnk, phr);

+    return pUnkRet;

+}

+

+CMemAllocator::CMemAllocator(

+    __in_opt LPCTSTR pName,

+    __inout_opt LPUNKNOWN pUnk,

+    __inout HRESULT *phr)

+    : CBaseAllocator(pName, pUnk, phr, TRUE, TRUE),

+    m_pBuffer(NULL)

+{

+}

+

+#ifdef UNICODE

+CMemAllocator::CMemAllocator(

+    __in_opt LPCSTR pName,

+    __inout_opt LPUNKNOWN pUnk,

+    __inout HRESULT *phr)

+    : CBaseAllocator(pName, pUnk, phr, TRUE, TRUE),

+    m_pBuffer(NULL)

+{

+}

+#endif

+

+/* This sets the size and count of the required samples. The memory isn't

+   actually allocated until Commit() is called, if memory has already been

+   allocated then assuming no samples are outstanding the user may call us

+   to change the buffering, the memory will be released in Commit() */

+STDMETHODIMP

+CMemAllocator::SetProperties(

+                __in ALLOCATOR_PROPERTIES* pRequest,

+                __out ALLOCATOR_PROPERTIES* pActual)

+{

+    CheckPointer(pActual,E_POINTER);

+    ValidateReadWritePtr(pActual,sizeof(ALLOCATOR_PROPERTIES));

+    CAutoLock cObjectLock(this);

+

+    ZeroMemory(pActual, sizeof(ALLOCATOR_PROPERTIES));

+

+    ASSERT(pRequest->cbBuffer > 0);

+

+    SYSTEM_INFO SysInfo;

+    GetSystemInfo(&SysInfo);

+

+    /*  Check the alignment request is a power of 2 */

+    if ((-pRequest->cbAlign & pRequest->cbAlign) != pRequest->cbAlign) {

+        DbgLog((LOG_ERROR, 1, TEXT("Alignment requested 0x%x not a power of 2!"),

+               pRequest->cbAlign));

+    }

+    /*  Check the alignment requested */

+    if (pRequest->cbAlign == 0 ||

+    (SysInfo.dwAllocationGranularity & (pRequest->cbAlign - 1)) != 0) {

+        DbgLog((LOG_ERROR, 1, TEXT("Invalid alignment 0x%x requested - granularity = 0x%x"),

+               pRequest->cbAlign, SysInfo.dwAllocationGranularity));

+        return VFW_E_BADALIGN;

+    }

+

+    /* Can't do this if already committed, there is an argument that says we

+       should not reject the SetProperties call if there are buffers still

+       active. However this is called by the source filter, which is the same

+       person who is holding the samples. Therefore it is not unreasonable

+       for them to free all their samples before changing the requirements */

+

+    if (m_bCommitted == TRUE) {

+        return VFW_E_ALREADY_COMMITTED;

+    }

+

+    /* Must be no outstanding buffers */

+

+    if (m_lFree.GetCount() < m_lAllocated) {

+        return VFW_E_BUFFERS_OUTSTANDING;

+    }

+

+    /* There isn't any real need to check the parameters as they

+       will just be rejected when the user finally calls Commit */

+

+    // round length up to alignment - remember that prefix is included in

+    // the alignment

+    LONG lSize = pRequest->cbBuffer + pRequest->cbPrefix;

+    LONG lRemainder = lSize % pRequest->cbAlign;

+    if (lRemainder != 0) {

+        lSize = lSize - lRemainder + pRequest->cbAlign;

+    }

+    pActual->cbBuffer = m_lSize = (lSize - pRequest->cbPrefix);

+

+    pActual->cBuffers = m_lCount = pRequest->cBuffers;

+    pActual->cbAlign = m_lAlignment = pRequest->cbAlign;

+    pActual->cbPrefix = m_lPrefix = pRequest->cbPrefix;

+

+    m_bChanged = TRUE;

+    return NOERROR;

+}

+

+// override this to allocate our resources when Commit is called.

+//

+// note that our resources may be already allocated when this is called,

+// since we don't free them on Decommit. We will only be called when in

+// decommit state with all buffers free.

+//

+// object locked by caller

+HRESULT

+CMemAllocator::Alloc(void)

+{

+    CAutoLock lck(this);

+

+    /* Check he has called SetProperties */

+    HRESULT hr = CBaseAllocator::Alloc();

+    if (FAILED(hr)) {

+        return hr;

+    }

+

+    /* If the requirements haven't changed then don't reallocate */

+    if (hr == S_FALSE) {

+        ASSERT(m_pBuffer);

+        return NOERROR;

+    }

+    ASSERT(hr == S_OK); // we use this fact in the loop below

+

+    /* Free the old resources */

+    if (m_pBuffer) {

+        ReallyFree();

+    }

+

+    /* Make sure we've got reasonable values */

+    if ( m_lSize < 0 || m_lPrefix < 0 || m_lCount < 0 ) {

+        return E_OUTOFMEMORY;

+    }

+

+    /* Compute the aligned size */

+    LONG lAlignedSize = m_lSize + m_lPrefix;

+

+    /*  Check overflow */

+    if (lAlignedSize < m_lSize) {

+        return E_OUTOFMEMORY;

+    }

+

+    if (m_lAlignment > 1) {

+        LONG lRemainder = lAlignedSize % m_lAlignment;

+        if (lRemainder != 0) {

+            LONG lNewSize = lAlignedSize + m_lAlignment - lRemainder;

+            if (lNewSize < lAlignedSize) {

+                return E_OUTOFMEMORY;

+            }

+            lAlignedSize = lNewSize;

+        }

+    }

+

+    /* Create the contiguous memory block for the samples

+       making sure it's properly aligned (64K should be enough!)

+    */

+    ASSERT(lAlignedSize % m_lAlignment == 0);

+

+    LONGLONG lToAllocate = m_lCount * (LONGLONG)lAlignedSize;

+

+    /*  Check overflow */

+    if (lToAllocate > MAXLONG) {

+        return E_OUTOFMEMORY;

+    }

+

+    m_pBuffer = (PBYTE)VirtualAlloc(NULL,

+                    (LONG)lToAllocate,

+                    MEM_COMMIT,

+                    PAGE_READWRITE);

+

+    if (m_pBuffer == NULL) {

+        return E_OUTOFMEMORY;

+    }

+

+    LPBYTE pNext = m_pBuffer;

+    CMediaSample *pSample;

+

+    ASSERT(m_lAllocated == 0);

+

+    // Create the new samples - we have allocated m_lSize bytes for each sample

+    // plus m_lPrefix bytes per sample as a prefix. We set the pointer to

+    // the memory after the prefix - so that GetPointer() will return a pointer

+    // to m_lSize bytes.

+    for (; m_lAllocated < m_lCount; m_lAllocated++, pNext += lAlignedSize) {

+

+

+        pSample = new CMediaSample(

+                            NAME("Default memory media sample"),

+                this,

+                            &hr,

+                            pNext + m_lPrefix,      // GetPointer() value

+                            m_lSize);               // not including prefix

+

+            ASSERT(SUCCEEDED(hr));

+        if (pSample == NULL) {

+            return E_OUTOFMEMORY;

+        }

+

+        // This CANNOT fail

+        m_lFree.Add(pSample);

+    }

+

+    m_bChanged = FALSE;

+    return NOERROR;

+}

+

+

+// override this to free up any resources we have allocated.

+// called from the base class on Decommit when all buffers have been

+// returned to the free list.

+//

+// caller has already locked the object.

+

+// in our case, we keep the memory until we are deleted, so

+// we do nothing here. The memory is deleted in the destructor by

+// calling ReallyFree()

+void

+CMemAllocator::Free(void)

+{

+    return;

+}

+

+

+// called from the destructor (and from Alloc if changing size/count) to

+// actually free up the memory

+void

+CMemAllocator::ReallyFree(void)

+{

+    /* Should never be deleting this unless all buffers are freed */

+

+    ASSERT(m_lAllocated == m_lFree.GetCount());

+

+    /* Free up all the CMediaSamples */

+

+    CMediaSample *pSample;

+    for (;;) {

+        pSample = m_lFree.RemoveHead();

+        if (pSample != NULL) {

+            delete pSample;

+        } else {

+            break;

+        }

+    }

+

+    m_lAllocated = 0;

+

+    // free the block of buffer memory

+    if (m_pBuffer) {

+        EXECUTE_ASSERT(VirtualFree(m_pBuffer, 0, MEM_RELEASE));

+        m_pBuffer = NULL;

+    }

+}

+

+

+/* Destructor frees our memory resources */

+

+CMemAllocator::~CMemAllocator()

+{

+    Decommit();

+    ReallyFree();

+}

+

+// ------------------------------------------------------------------------

+// filter registration through IFilterMapper. used if IFilterMapper is

+// not found (Quartz 1.0 install)

+

+STDAPI

+AMovieSetupRegisterFilter( const AMOVIESETUP_FILTER * const psetupdata

+                         , IFilterMapper *                  pIFM

+                         , BOOL                             bRegister  )

+{

+  DbgLog((LOG_TRACE, 3, TEXT("= AMovieSetupRegisterFilter")));

+

+  // check we've got data

+  //

+  if( NULL == psetupdata ) return S_FALSE;

+

+

+  // unregister filter

+  // (as pins are subkeys of filter's CLSID key

+  // they do not need to be removed separately).

+  //

+  DbgLog((LOG_TRACE, 3, TEXT("= = unregister filter")));

+  HRESULT hr = pIFM->UnregisterFilter( *(psetupdata->clsID) );

+

+

+  if( bRegister )

+  {

+    // register filter

+    //

+    DbgLog((LOG_TRACE, 3, TEXT("= = register filter")));

+    hr = pIFM->RegisterFilter( *(psetupdata->clsID)

+                             , psetupdata->strName

+                             , psetupdata->dwMerit    );

+    if( SUCCEEDED(hr) )

+    {

+      // all its pins

+      //

+      DbgLog((LOG_TRACE, 3, TEXT("= = register filter pins")));

+      for( UINT m1=0; m1 < psetupdata->nPins; m1++ )

+      {

+        hr = pIFM->RegisterPin( *(psetupdata->clsID)

+                              , psetupdata->lpPin[m1].strName

+                              , psetupdata->lpPin[m1].bRendered

+                              , psetupdata->lpPin[m1].bOutput

+                              , psetupdata->lpPin[m1].bZero

+                              , psetupdata->lpPin[m1].bMany

+                              , *(psetupdata->lpPin[m1].clsConnectsToFilter)

+                              , psetupdata->lpPin[m1].strConnectsToPin );

+

+        if( SUCCEEDED(hr) )

+        {

+          // and each pin's media types

+          //

+          DbgLog((LOG_TRACE, 3, TEXT("= = register filter pin types")));

+          for( UINT m2=0; m2 < psetupdata->lpPin[m1].nMediaTypes; m2++ )

+          {

+            hr = pIFM->RegisterPinType( *(psetupdata->clsID)

+                                      , psetupdata->lpPin[m1].strName

+                                      , *(psetupdata->lpPin[m1].lpMediaType[m2].clsMajorType)

+                                      , *(psetupdata->lpPin[m1].lpMediaType[m2].clsMinorType) );

+            if( FAILED(hr) ) break;

+          }

+          if( FAILED(hr) ) break;

+        }

+        if( FAILED(hr) ) break;

+      }

+    }

+  }

+

+  // handle one acceptable "error" - that

+  // of filter not being registered!

+  // (couldn't find a suitable #define'd

+  // name for the error!)

+  //

+  if( 0x80070002 == hr)

+    return NOERROR;

+  else

+    return hr;

+}

+

+//  Remove warnings about unreferenced inline functions

+#pragma warning(disable:4514)

+

+#endif /* PJMEDIA_VIDEO_DEV_HAS_DSHOW */