* #27232: jni: added pjproject checkout as regular git content

We will remove it once the next release of pjsip (with Android support)
comes out and is merged into SFLphone.
diff --git a/jni/pjproject-android/third_party/BaseClasses/amfilter.cpp b/jni/pjproject-android/third_party/BaseClasses/amfilter.cpp
new file mode 100644
index 0000000..282c35d
--- /dev/null
+++ b/jni/pjproject-android/third_party/BaseClasses/amfilter.cpp
@@ -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 */

diff --git a/jni/pjproject-android/third_party/BaseClasses/amfilter.h b/jni/pjproject-android/third_party/BaseClasses/amfilter.h
new file mode 100644
index 0000000..14f17cd
--- /dev/null
+++ b/jni/pjproject-android/third_party/BaseClasses/amfilter.h
@@ -0,0 +1,1587 @@
+//------------------------------------------------------------------------------

+// File: AMFilter.h

+//

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

+//       architecture.

+//

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

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

+

+

+#ifndef __FILTER__

+#define __FILTER__

+

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

+

+class CBaseMediaFilter;     // IMediaFilter support

+class CBaseFilter;          // IBaseFilter,IMediaFilter support

+class CBasePin;             // Abstract base class for IPin interface

+class CEnumPins;            // Enumerate input and output pins

+class CEnumMediaTypes;      // Enumerate the pin's preferred formats

+class CBaseOutputPin;       // Adds data provider member functions

+class CBaseInputPin;        // Implements IMemInputPin interface

+class CMediaSample;         // Basic transport unit for IMemInputPin

+class CBaseAllocator;       // General list guff for most allocators

+class CMemAllocator;        // Implements memory buffer allocation

+

+

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

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

+//

+// QueryFilterInfo and QueryPinInfo AddRef the interface pointers

+// they return.  You can use the macro below to release the interface.

+//

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

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

+

+#define QueryFilterInfoReleaseGraph(fi) if ((fi).pGraph) (fi).pGraph->Release();

+

+#define QueryPinInfoReleaseFilter(pi) if ((pi).pFilter) (pi).pFilter->Release();

+

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

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

+// Defines CBaseMediaFilter

+//

+// Abstract base class implementing IMediaFilter.

+//

+// Typically you will derive your filter from CBaseFilter rather than

+// this,  unless you are implementing an object such as a plug-in

+// distributor that needs to support IMediaFilter but not IBaseFilter.

+//

+// Note that IMediaFilter is derived from IPersist to allow query of

+// class id.

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

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

+

+class AM_NOVTABLE CBaseMediaFilter : public CUnknown,

+                                     public IMediaFilter

+{

+

+protected:

+

+    FILTER_STATE    m_State;            // current state: running, paused

+    IReferenceClock *m_pClock;          // this filter's reference clock

+    // note: all filters in a filter graph use the same clock

+

+    // offset from stream time to reference time

+    CRefTime        m_tStart;

+

+    CLSID	    m_clsid;            // This filters clsid

+                                        // used for serialization

+    CCritSec        *m_pLock;           // Object we use for locking

+

+public:

+

+    CBaseMediaFilter(

+        __in_opt LPCTSTR pName,

+        __inout_opt LPUNKNOWN pUnk,

+        __in CCritSec  *pLock,

+	REFCLSID   clsid);

+

+    virtual ~CBaseMediaFilter();

+

+    DECLARE_IUNKNOWN

+

+    // override this to say what interfaces we support where

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

+

+    //

+    // --- IPersist method ---

+    //

+

+    STDMETHODIMP GetClassID(__out CLSID *pClsID);

+

+    // --- IMediaFilter methods ---

+

+    STDMETHODIMP GetState(DWORD dwMSecs, __out FILTER_STATE *State);

+

+    STDMETHODIMP SetSyncSource(__inout_opt IReferenceClock *pClock);

+

+    STDMETHODIMP GetSyncSource(__deref_out_opt IReferenceClock **pClock);

+

+    // default implementation of Stop and Pause just record the

+    // state. Override to activate or de-activate your filter.

+    // Note that Run when called from Stopped state will call Pause

+    // to ensure activation, so if you are a source or transform

+    // you will probably not need to override Run.

+    STDMETHODIMP Stop();

+    STDMETHODIMP Pause();

+

+

+    // the start parameter is the difference to be added to the

+    // sample's stream time to get the reference time for

+    // its presentation

+    STDMETHODIMP Run(REFERENCE_TIME tStart);

+

+    // --- helper methods ---

+

+    // return the current stream time - ie find out what

+    // stream time should be appearing now

+    virtual HRESULT StreamTime(CRefTime& rtStream);

+

+    // Is the filter currently active? (running or paused)

+    BOOL IsActive() {

+        CAutoLock cObjectLock(m_pLock);

+        return ((m_State == State_Paused) || (m_State == State_Running));

+    };

+};

+

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

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

+// Defines CBaseFilter

+//

+// An abstract class providing basic IBaseFilter support for pin

+// enumeration and filter information reading.

+//

+// We cannot derive from CBaseMediaFilter since methods in IMediaFilter

+// are also in IBaseFilter and would be ambiguous. Since much of the code

+// assumes that they derive from a class that has m_State and other state

+// directly available, we duplicate code from CBaseMediaFilter rather than

+// having a member variable.

+//

+// Derive your filter from this, or from a derived object such as

+// CTransformFilter.

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

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

+

+

+class AM_NOVTABLE CBaseFilter : public CUnknown,        // Handles an IUnknown

+                    public IBaseFilter,     // The Filter Interface

+                    public IAMovieSetup     // For un/registration

+{

+

+friend class CBasePin;

+

+protected:

+    FILTER_STATE    m_State;            // current state: running, paused

+    IReferenceClock *m_pClock;          // this graph's ref clock

+    CRefTime        m_tStart;           // offset from stream time to reference time

+    CLSID	    m_clsid;            // This filters clsid

+                                        // used for serialization

+    CCritSec        *m_pLock;           // Object we use for locking

+

+    WCHAR           *m_pName;           // Full filter name

+    IFilterGraph    *m_pGraph;          // Graph we belong to

+    IMediaEventSink *m_pSink;           // Called with notify events

+    LONG            m_PinVersion;       // Current pin version

+

+public:

+

+    CBaseFilter(

+        __in_opt LPCTSTR pName,   // Object description

+        __inout_opt LPUNKNOWN pUnk,  // IUnknown of delegating object

+        __in CCritSec  *pLock,    // Object who maintains lock

+	REFCLSID   clsid);        // The clsid to be used to serialize this filter

+

+    CBaseFilter(

+        __in_opt LPCTSTR pName,    // Object description

+        __in_opt LPUNKNOWN pUnk,  // IUnknown of delegating object

+        __in CCritSec  *pLock,    // Object who maintains lock

+	REFCLSID   clsid,         // The clsid to be used to serialize this filter

+        __inout HRESULT   *phr);  // General OLE return code

+#ifdef UNICODE

+    CBaseFilter(

+        __in_opt LPCSTR pName,    // Object description

+        __in_opt LPUNKNOWN pUnk,  // IUnknown of delegating object

+        __in CCritSec  *pLock,    // Object who maintains lock

+	REFCLSID   clsid);        // The clsid to be used to serialize this filter

+

+    CBaseFilter(

+        __in_opt LPCSTR pName,     // Object description

+        __in_opt LPUNKNOWN pUnk,  // IUnknown of delegating object

+        __in CCritSec  *pLock,    // Object who maintains lock

+	REFCLSID   clsid,         // The clsid to be used to serialize this filter

+        __inout HRESULT   *phr);  // General OLE return code

+#endif

+    ~CBaseFilter();

+

+    DECLARE_IUNKNOWN

+

+    // override this to say what interfaces we support where

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

+#ifdef DEBUG

+    STDMETHODIMP_(ULONG) NonDelegatingRelease();

+#endif

+

+    //

+    // --- IPersist method ---

+    //

+

+    STDMETHODIMP GetClassID(__out CLSID *pClsID);

+

+    // --- IMediaFilter methods ---

+

+    STDMETHODIMP GetState(DWORD dwMSecs, __out FILTER_STATE *State);

+

+    STDMETHODIMP SetSyncSource(__in_opt IReferenceClock *pClock);

+

+    STDMETHODIMP GetSyncSource(__deref_out_opt IReferenceClock **pClock);

+

+

+    // override Stop and Pause so we can activate the pins.

+    // Note that Run will call Pause first if activation needed.

+    // Override these if you want to activate your filter rather than

+    // your pins.

+    STDMETHODIMP Stop();

+    STDMETHODIMP Pause();

+

+    // the start parameter is the difference to be added to the

+    // sample's stream time to get the reference time for

+    // its presentation

+    STDMETHODIMP Run(REFERENCE_TIME tStart);

+

+    // --- helper methods ---

+

+    // return the current stream time - ie find out what

+    // stream time should be appearing now

+    virtual HRESULT StreamTime(CRefTime& rtStream);

+

+    // Is the filter currently active?

+    BOOL IsActive() {

+        CAutoLock cObjectLock(m_pLock);

+        return ((m_State == State_Paused) || (m_State == State_Running));

+    };

+

+    // Is this filter stopped (without locking)

+    BOOL IsStopped() {

+        return (m_State == State_Stopped);

+    };

+

+    //

+    // --- IBaseFilter methods ---

+    //

+

+    // pin enumerator

+    STDMETHODIMP EnumPins(

+                    __deref_out IEnumPins ** ppEnum);

+

+

+    // default behaviour of FindPin assumes pin ids are their names

+    STDMETHODIMP FindPin(

+        LPCWSTR Id,

+        __deref_out IPin ** ppPin

+    );

+

+    STDMETHODIMP QueryFilterInfo(

+                    __out FILTER_INFO * pInfo);

+

+    STDMETHODIMP JoinFilterGraph(

+                    __inout_opt IFilterGraph * pGraph,

+                    __in_opt LPCWSTR pName);

+

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

+    // memory returned should be freed using CoTaskMemFree

+    // default implementation returns E_NOTIMPL

+    STDMETHODIMP QueryVendorInfo(

+                    __deref_out LPWSTR* pVendorInfo

+            );

+

+    // --- helper methods ---

+

+    // 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 NotifyEvent(

+        long EventCode,

+        LONG_PTR EventParam1,

+        LONG_PTR EventParam2);

+

+    // return the filter graph we belong to

+    __out_opt IFilterGraph *GetFilterGraph() {

+        return m_pGraph;

+    }

+

+    // 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 ReconnectPin(IPin *pPin, __in_opt AM_MEDIA_TYPE const *pmt);

+

+    // find out the current pin version (used by enumerators)

+    virtual LONG GetPinVersion();

+    void IncrementPinVersion();

+

+    // you need to supply these to access the pins from the enumerator

+    // and for default Stop and Pause/Run activation.

+    virtual int GetPinCount() PURE;

+    virtual CBasePin *GetPin(int n) PURE;

+

+    // --- IAMovieSetup methods ---

+

+    STDMETHODIMP Register();    // ask filter to register itself

+    STDMETHODIMP Unregister();  // and unregister itself

+

+    // --- setup helper methods ---

+    // (override to return filters setup data)

+

+    virtual __out_opt LPAMOVIESETUP_FILTER GetSetupData(){ return NULL; }

+

+};

+

+

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

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

+// Defines CBasePin

+//

+// Abstract class that supports the basics of IPin

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

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

+

+class  AM_NOVTABLE CBasePin : public CUnknown, public IPin, public IQualityControl

+{

+

+protected:

+

+    WCHAR *         m_pName;		        // This pin's name

+    IPin            *m_Connected;               // Pin we have connected to

+    PIN_DIRECTION   m_dir;                      // Direction of this pin

+    CCritSec        *m_pLock;                   // Object we use for locking

+    bool            m_bRunTimeError;            // Run time error generated

+    bool            m_bCanReconnectWhenActive;  // OK to reconnect when active

+    bool            m_bTryMyTypesFirst;         // When connecting enumerate

+                                                // this pin's types first

+    CBaseFilter    *m_pFilter;                  // Filter we were created by

+    IQualityControl *m_pQSink;                  // Target for Quality messages

+    LONG            m_TypeVersion;              // Holds current type version

+    CMediaType      m_mt;                       // Media type of connection

+

+    CRefTime        m_tStart;                   // time from NewSegment call

+    CRefTime        m_tStop;                    // time from NewSegment

+    double          m_dRate;                    // rate from NewSegment

+

+#ifdef DEBUG

+    LONG            m_cRef;                     // Ref count tracing

+#endif

+

+    // displays pin connection information

+

+#ifdef DEBUG

+    void DisplayPinInfo(IPin *pReceivePin);

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

+#else

+    void DisplayPinInfo(IPin *pReceivePin) {};

+    void DisplayTypeInfo(IPin *pPin, const CMediaType *pmt) {};

+#endif

+

+    // used to agree a media type for a pin connection

+

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

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

+    HRESULT

+    AttemptConnection(

+        IPin* pReceivePin,      // connect to this pin

+        const CMediaType* pmt   // using this type

+    );

+

+    // try all the media types in this enumerator - for each that

+    // we accept, try to connect using ReceiveConnection.

+    HRESULT TryMediaTypes(

+                        IPin *pReceivePin,          // connect to this pin

+                        __in_opt const CMediaType *pmt,  // proposed type from Connect

+                        IEnumMediaTypes *pEnum);    // try this enumerator

+

+    // establish a connection with a suitable mediatype. Needs to

+    // propose a media type if the pmt pointer is null or partially

+    // specified - use TryMediaTypes on both our and then the other pin's

+    // enumerator until we find one that works.

+    HRESULT AgreeMediaType(

+                        IPin *pReceivePin,      // connect to this pin

+                        const CMediaType *pmt);      // proposed type from Connect

+

+public:

+

+    CBasePin(

+        __in_opt LPCTSTR pObjectName,         // Object description

+        __in CBaseFilter *pFilter,       // Owning filter who knows about pins

+        __in CCritSec *pLock,            // Object who implements the lock

+        __inout HRESULT *phr,               // General OLE return code

+        __in_opt LPCWSTR pName,              // Pin name for us

+        PIN_DIRECTION dir);         // Either PINDIR_INPUT or PINDIR_OUTPUT

+#ifdef UNICODE

+    CBasePin(

+        __in_opt LPCSTR pObjectName,         // Object description

+        __in CBaseFilter *pFilter,       // Owning filter who knows about pins

+        __in CCritSec *pLock,            // Object who implements the lock

+        __inout HRESULT *phr,               // General OLE return code

+        __in_opt LPCWSTR pName,              // Pin name for us

+        PIN_DIRECTION dir);         // Either PINDIR_INPUT or PINDIR_OUTPUT

+#endif

+    virtual ~CBasePin();

+

+    DECLARE_IUNKNOWN

+

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

+    STDMETHODIMP_(ULONG) NonDelegatingRelease();

+    STDMETHODIMP_(ULONG) NonDelegatingAddRef();

+

+    // --- IPin methods ---

+

+    // take lead role in establishing a connection. Media type pointer

+    // may be null, or may point to partially-specified mediatype

+    // (subtype or format type may be GUID_NULL).

+    STDMETHODIMP Connect(

+        IPin * pReceivePin,

+        __in_opt const AM_MEDIA_TYPE *pmt   // optional media type

+    );

+

+    // (passive) accept a connection from another pin

+    STDMETHODIMP ReceiveConnection(

+        IPin * pConnector,      // this is the initiating connecting pin

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

+    );

+

+    STDMETHODIMP Disconnect();

+

+    STDMETHODIMP ConnectedTo(__deref_out IPin **pPin);

+

+    STDMETHODIMP ConnectionMediaType(__out AM_MEDIA_TYPE *pmt);

+

+    STDMETHODIMP QueryPinInfo(

+        __out PIN_INFO * pInfo

+    );

+

+    STDMETHODIMP QueryDirection(

+    	__out PIN_DIRECTION * pPinDir

+    );

+

+    STDMETHODIMP QueryId(

+        __deref_out LPWSTR * Id

+    );

+

+    // does the pin support this media type

+    STDMETHODIMP QueryAccept(

+        const AM_MEDIA_TYPE *pmt

+    );

+

+    // return an enumerator for this pins preferred media types

+    STDMETHODIMP EnumMediaTypes(

+        __deref_out IEnumMediaTypes **ppEnum

+    );

+

+    // return an array of IPin* - the pins that this pin internally connects to

+    // All pins put in the array must be AddReffed (but no others)

+    // Errors: "Can't say" - FAIL, not enough slots - return S_FALSE

+    // Default: return E_NOTIMPL

+    // The filter graph will interpret NOT_IMPL as any input pin connects to

+    // all visible output pins and vice versa.

+    // apPin can be NULL if nPin==0 (not otherwise).

+    STDMETHODIMP QueryInternalConnections(

+        __out_ecount_part(*nPin,*nPin) IPin* *apPin,     // array of IPin*

+        __inout ULONG *nPin                  // on input, the number of slots

+                                             // on output  the number of pins

+    ) { return E_NOTIMPL; }

+

+    // Called when no more data will be sent

+    STDMETHODIMP EndOfStream(void);

+

+    // Begin/EndFlush still PURE

+

+    // 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 NewSegment(

+                    REFERENCE_TIME tStart,

+                    REFERENCE_TIME tStop,

+                    double dRate);

+

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

+    // IQualityControl methods

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

+

+    STDMETHODIMP Notify(IBaseFilter * pSender, Quality q);

+

+    STDMETHODIMP SetSink(IQualityControl * piqc);

+

+    // --- helper methods ---

+

+    // Returns true if the pin is connected. false otherwise.

+    BOOL IsConnected(void) {return (m_Connected != NULL); };

+    // Return the pin this is connected to (if any)

+    IPin * GetConnected() { return m_Connected; };

+

+    // Check if our filter is currently stopped

+    BOOL IsStopped() {

+        return (m_pFilter->m_State == State_Stopped);

+    };

+

+    // find out the current type version (used by enumerators)

+    virtual LONG GetMediaTypeVersion();

+    void IncrementTypeVersion();

+

+    // switch the pin to active (paused or running) mode

+    // not an error to call this if already active

+    virtual HRESULT Active(void);

+

+    // switch the pin to inactive state - may already be inactive

+    virtual HRESULT Inactive(void);

+

+    // Notify of Run() from filter

+    virtual HRESULT Run(REFERENCE_TIME tStart);

+

+    // check if the pin can support this specific proposed type and format

+    virtual HRESULT CheckMediaType(const CMediaType *) PURE;

+

+    // set the connection to use this format (previously agreed)

+    virtual HRESULT SetMediaType(const CMediaType *);

+

+    // check that the connection is ok before verifying it

+    // can be overridden eg to check what interfaces will be supported.

+    virtual HRESULT CheckConnect(IPin *);

+

+    // Set and release resources required for a connection

+    virtual HRESULT BreakConnect();

+    virtual HRESULT CompleteConnect(IPin *pReceivePin);

+

+    // returns the preferred formats for a pin

+    virtual HRESULT GetMediaType(int iPosition, __inout CMediaType *pMediaType);

+

+    // access to NewSegment values

+    REFERENCE_TIME CurrentStopTime() {

+        return m_tStop;

+    }

+    REFERENCE_TIME CurrentStartTime() {

+        return m_tStart;

+    }

+    double CurrentRate() {

+        return m_dRate;

+    }

+

+    //  Access name

+    LPWSTR Name() { return m_pName; };

+

+    //  Can reconnectwhen active?

+    void SetReconnectWhenActive(bool bCanReconnect)

+    {

+        m_bCanReconnectWhenActive = bCanReconnect;

+    }

+

+    bool CanReconnectWhenActive()

+    {

+        return m_bCanReconnectWhenActive;

+    }

+

+protected:

+    STDMETHODIMP DisconnectInternal();

+};

+

+

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

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

+// Defines CEnumPins

+//

+// Pin enumerator class that works by calling CBaseFilter. This interface

+// is provided by CBaseFilter::EnumPins and calls GetPinCount() and

+// GetPin() to enumerate existing pins. Needs to be a separate object so

+// that it can be cloned (creating an existing object at the same

+// position in the enumeration)

+//

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

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

+

+class CEnumPins : public IEnumPins      // The interface we support

+{

+    int m_Position;                 // Current ordinal position

+    int m_PinCount;                 // Number of pins available

+    CBaseFilter *m_pFilter;         // The filter who owns us

+    LONG m_Version;                 // Pin version information

+    LONG m_cRef;

+

+    typedef CGenericList<CBasePin> CPinList;

+

+    CPinList m_PinCache;	    // These pointers have not been AddRef'ed and

+				    // so they should not be dereferenced.  They are

+				    // merely kept to ID which pins have been enumerated.

+

+#ifdef DEBUG

+    DWORD m_dwCookie;

+#endif

+

+    /* If while we are retrieving a pin for example from the filter an error

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

+       filter (someone may have deleted all the pins). We can check before

+       starting whether or not the operation is likely to fail by asking the

+       filter what it's current version number is. If the filter has not

+       overriden the GetPinVersion method then this will always match */

+

+    BOOL AreWeOutOfSync() {

+        return (m_pFilter->GetPinVersion() == m_Version ? FALSE : TRUE);

+    };

+

+    /* This method performs the same operations as Reset, except is does not clear

+       the cache of pins already enumerated. */

+

+    STDMETHODIMP Refresh();

+

+public:

+

+    CEnumPins(

+        __in CBaseFilter *pFilter,

+        __in_opt CEnumPins *pEnumPins);

+

+    virtual ~CEnumPins();

+

+    // IUnknown

+    STDMETHODIMP QueryInterface(REFIID riid, __deref_out void **ppv);

+    STDMETHODIMP_(ULONG) AddRef();

+    STDMETHODIMP_(ULONG) Release();

+

+    // IEnumPins

+    STDMETHODIMP Next(

+        ULONG cPins,         // place this many pins...

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

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

+    );

+

+    STDMETHODIMP Skip(ULONG cPins);

+    STDMETHODIMP Reset();

+    STDMETHODIMP Clone(__deref_out IEnumPins **ppEnum);

+

+

+};

+

+

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

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

+// Defines CEnumMediaTypes

+//

+// Enumerates the preferred formats for input and output pins

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

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

+

+class CEnumMediaTypes : public IEnumMediaTypes    // The interface we support

+{

+    int m_Position;           // Current ordinal position

+    CBasePin *m_pPin;         // The pin who owns us

+    LONG m_Version;           // Media type version value

+    LONG m_cRef;

+#ifdef DEBUG

+    DWORD m_dwCookie;

+#endif

+

+    /* The media types a filter supports can be quite dynamic so we add to

+       the general IEnumXXXX interface the ability to be signaled when they

+       change via an event handle the connected filter supplies. Until the

+       Reset method is called after the state changes all further calls to

+       the enumerator (except Reset) will return E_UNEXPECTED error code */

+

+    BOOL AreWeOutOfSync() {

+        return (m_pPin->GetMediaTypeVersion() == m_Version ? FALSE : TRUE);

+    };

+

+public:

+

+    CEnumMediaTypes(

+        __in CBasePin *pPin,

+        __in_opt CEnumMediaTypes *pEnumMediaTypes);

+

+    virtual ~CEnumMediaTypes();

+

+    // IUnknown

+    STDMETHODIMP QueryInterface(REFIID riid, __deref_out void **ppv);

+    STDMETHODIMP_(ULONG) AddRef();

+    STDMETHODIMP_(ULONG) Release();

+

+    // IEnumMediaTypes

+    STDMETHODIMP Next(

+        ULONG cMediaTypes,          // place this many pins...

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

+        __out_opt ULONG * pcFetched           // actual count passed

+    );

+

+    STDMETHODIMP Skip(ULONG cMediaTypes);

+    STDMETHODIMP Reset();

+    STDMETHODIMP Clone(__deref_out IEnumMediaTypes **ppEnum);

+};

+

+

+

+

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

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

+// Defines CBaseOutputPin

+//

+// class derived from CBasePin that can pass buffers to a connected pin

+// that supports IMemInputPin. Supports IPin.

+//

+// Derive your output pin from this.

+//

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

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

+

+class  AM_NOVTABLE CBaseOutputPin : public CBasePin

+{

+

+protected:

+

+    IMemAllocator *m_pAllocator;

+    IMemInputPin *m_pInputPin;        // interface on the downstreaminput pin

+                                      // set up in CheckConnect when we connect.

+

+public:

+

+    CBaseOutputPin(

+        __in_opt LPCTSTR pObjectName,

+        __in CBaseFilter *pFilter,

+        __in CCritSec *pLock,

+        __inout HRESULT *phr,

+        __in_opt LPCWSTR pName);

+#ifdef UNICODE

+    CBaseOutputPin(

+        __in_opt LPCSTR pObjectName,

+        __in CBaseFilter *pFilter,

+        __in CCritSec *pLock,

+        __inout HRESULT *phr,

+        __in_opt LPCWSTR pName);

+#endif

+    // override CompleteConnect() so we can negotiate an allocator

+    virtual HRESULT CompleteConnect(IPin *pReceivePin);

+

+    // negotiate the allocator and its buffer size/count and other properties

+    // Calls DecideBufferSize to set properties

+    virtual HRESULT DecideAllocator(IMemInputPin * pPin, __deref_out IMemAllocator ** pAlloc);

+

+    // override this to set the buffer size and count. Return an error

+    // if the size/count is not to your liking.

+    // The allocator properties passed in are those requested by the

+    // input pin - use eg the alignment and prefix members if you have

+    // no preference on these.

+    virtual HRESULT DecideBufferSize(

+        IMemAllocator * pAlloc,

+        __inout ALLOCATOR_PROPERTIES * ppropInputRequest

+    ) PURE;

+

+    // returns an empty sample buffer from the allocator

+    virtual HRESULT GetDeliveryBuffer(__deref_out IMediaSample ** ppSample,

+                                      __in_opt REFERENCE_TIME * pStartTime,

+                                      __in_opt REFERENCE_TIME * pEndTime,

+                                      DWORD dwFlags);

+

+    // deliver a filled-in sample to the connected input pin

+    // note - you need to release it after calling this. The receiving

+    // pin will addref the sample if it needs to hold it beyond the

+    // call.

+    virtual HRESULT Deliver(IMediaSample *);

+

+    // override this to control the connection

+    virtual HRESULT InitAllocator(__deref_out IMemAllocator **ppAlloc);

+    HRESULT CheckConnect(IPin *pPin);

+    HRESULT BreakConnect();

+

+    // override to call Commit and Decommit

+    HRESULT Active(void);

+    HRESULT Inactive(void);

+

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

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

+    STDMETHODIMP EndOfStream(void);

+

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

+    // our connected input pin

+    virtual HRESULT DeliverEndOfStream(void);

+

+    // same for Begin/EndFlush - we handle Begin/EndFlush since it

+    // is an error on an output pin, and we have Deliver methods to

+    // call the methods on the connected pin

+    STDMETHODIMP BeginFlush(void);

+    STDMETHODIMP EndFlush(void);

+    virtual HRESULT DeliverBeginFlush(void);

+    virtual HRESULT DeliverEndFlush(void);

+

+    // deliver NewSegment to connected pin - you will need to

+    // override this if you queue any data in your output pin.

+    virtual HRESULT DeliverNewSegment(

+                        REFERENCE_TIME tStart,

+                        REFERENCE_TIME tStop,

+                        double dRate);

+

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

+    // IQualityControl methods

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

+

+    // All inherited from CBasePin and not overridden here.

+    // STDMETHODIMP Notify(IBaseFilter * pSender, Quality q);

+    // STDMETHODIMP SetSink(IQualityControl * piqc);

+};

+

+

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

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

+// Defines CBaseInputPin

+//

+// derive your standard input pin from this.

+// you need to supply GetMediaType and CheckConnect etc (see CBasePin),

+// and you need to supply Receive to do something more useful.

+//

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

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

+

+class AM_NOVTABLE CBaseInputPin : public CBasePin,

+                                  public IMemInputPin

+{

+

+protected:

+

+    IMemAllocator *m_pAllocator;    // Default memory allocator

+

+    // allocator is read-only, so received samples

+    // cannot be modified (probably only relevant to in-place

+    // transforms

+    BYTE m_bReadOnly;

+

+    // in flushing state (between BeginFlush and EndFlush)

+    // if TRUE, all Receives are returned with S_FALSE

+    BYTE m_bFlushing;

+

+    // Sample properties - initalized in Receive

+    AM_SAMPLE2_PROPERTIES m_SampleProps;

+

+public:

+

+    CBaseInputPin(

+        __in_opt LPCTSTR pObjectName,

+        __in CBaseFilter *pFilter,

+        __in CCritSec *pLock,

+        __inout HRESULT *phr,

+        __in_opt LPCWSTR pName);

+#ifdef UNICODE

+    CBaseInputPin(

+        __in_opt LPCSTR pObjectName,

+        __in CBaseFilter *pFilter,

+        __in CCritSec *pLock,

+        __inout HRESULT *phr,

+        __in_opt LPCWSTR pName);

+#endif

+    virtual ~CBaseInputPin();

+

+    DECLARE_IUNKNOWN

+

+    // override this to publicise our interfaces

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

+

+    // return the allocator interface that this input pin

+    // would like the output pin to use

+    STDMETHODIMP GetAllocator(__deref_out IMemAllocator ** ppAllocator);

+

+    // tell the input pin which allocator the output pin is actually

+    // going to use.

+    STDMETHODIMP NotifyAllocator(

+                    IMemAllocator * pAllocator,

+                    BOOL bReadOnly);

+

+    // do something with this media sample

+    STDMETHODIMP Receive(IMediaSample *pSample);

+

+    // do something with these media samples

+    STDMETHODIMP ReceiveMultiple (

+        __in_ecount(nSamples) IMediaSample **pSamples,

+        long nSamples,

+        __out long *nSamplesProcessed);

+

+    // See if Receive() blocks

+    STDMETHODIMP ReceiveCanBlock();

+

+    // 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 BeginFlush(void);

+

+    // 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 EndFlush(void);

+

+    // this method is optional (can return E_NOTIMPL).

+    // default implementation returns E_NOTIMPL. Override if you have

+    // specific alignment or prefix needs, but could use an upstream

+    // allocator

+    STDMETHODIMP GetAllocatorRequirements(__out ALLOCATOR_PROPERTIES*pProps);

+

+    // Release the pin's allocator.

+    HRESULT BreakConnect();

+

+    // helper method to check the read-only flag

+    BOOL IsReadOnly() {

+        return m_bReadOnly;

+    };

+

+    // helper method to see if we are flushing

+    BOOL IsFlushing() {

+        return m_bFlushing;

+    };

+

+    //  Override this for checking whether it's OK to process samples

+    //  Also call this from EndOfStream.

+    virtual HRESULT CheckStreaming();

+

+    // Pass a Quality notification on to the appropriate sink

+    HRESULT PassNotify(Quality& q);

+

+

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

+    // IQualityControl methods (from CBasePin)

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

+

+    STDMETHODIMP Notify(IBaseFilter * pSender, Quality q);

+

+    // no need to override:

+    // STDMETHODIMP SetSink(IQualityControl * piqc);

+

+

+    // switch the pin to inactive state - may already be inactive

+    virtual HRESULT Inactive(void);

+

+    // Return sample properties pointer

+    AM_SAMPLE2_PROPERTIES * SampleProps() {

+        ASSERT(m_SampleProps.cbData != 0);

+        return &m_SampleProps;

+    }

+

+};

+

+///////////////////////////////////////////////////////////////////////////

+// CDynamicOutputPin

+//

+

+class CDynamicOutputPin : public CBaseOutputPin,

+                          public IPinFlowControl

+{

+public:

+#ifdef UNICODE

+    CDynamicOutputPin(

+        __in_opt LPCSTR pObjectName,

+        __in CBaseFilter *pFilter,

+        __in CCritSec *pLock,

+        __inout HRESULT *phr,

+        __in_opt LPCWSTR pName);

+#endif

+

+    CDynamicOutputPin(

+        __in_opt LPCTSTR pObjectName,

+        __in CBaseFilter *pFilter,

+        __in CCritSec *pLock,

+        __inout HRESULT *phr,

+        __in_opt LPCWSTR pName);

+

+    ~CDynamicOutputPin();

+

+    // IUnknown Methods

+    DECLARE_IUNKNOWN

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

+

+    // IPin Methods

+    STDMETHODIMP Disconnect(void);

+

+    // IPinFlowControl Methods

+    STDMETHODIMP Block(DWORD dwBlockFlags, HANDLE hEvent);

+

+    //  Set graph config info

+    void SetConfigInfo(IGraphConfig *pGraphConfig, HANDLE hStopEvent);

+

+    #ifdef DEBUG

+    virtual HRESULT Deliver(IMediaSample *pSample);

+    virtual HRESULT DeliverEndOfStream(void);

+    virtual HRESULT DeliverNewSegment(REFERENCE_TIME tStart, REFERENCE_TIME tStop, double dRate);

+    #endif // DEBUG

+

+    HRESULT DeliverBeginFlush(void);

+    HRESULT DeliverEndFlush(void);

+

+    HRESULT Inactive(void);

+    HRESULT Active(void);

+    virtual HRESULT CompleteConnect(IPin *pReceivePin);

+

+    virtual HRESULT StartUsingOutputPin(void);

+    virtual void StopUsingOutputPin(void);

+    virtual bool StreamingThreadUsingOutputPin(void);

+

+    HRESULT ChangeOutputFormat

+        (

+        const AM_MEDIA_TYPE *pmt,

+        REFERENCE_TIME tSegmentStart,

+        REFERENCE_TIME tSegmentStop,

+        double dSegmentRate

+        );

+    HRESULT ChangeMediaType(const CMediaType *pmt);

+    HRESULT DynamicReconnect(const CMediaType *pmt);

+

+protected:

+    HRESULT SynchronousBlockOutputPin(void);

+    HRESULT AsynchronousBlockOutputPin(HANDLE hNotifyCallerPinBlockedEvent);

+    HRESULT UnblockOutputPin(void);

+

+    void BlockOutputPin(void);

+    void ResetBlockState(void);

+

+    static HRESULT WaitEvent(HANDLE hEvent);

+

+    enum BLOCK_STATE

+    {

+        NOT_BLOCKED,

+        PENDING,

+        BLOCKED

+    };

+

+    // This lock should be held when the following class members are

+    // being used: m_hNotifyCallerPinBlockedEvent, m_BlockState,

+    // m_dwBlockCallerThreadID and m_dwNumOutstandingOutputPinUsers.

+    CCritSec m_BlockStateLock;

+

+    // This event should be signaled when the output pin is

+    // not blocked.  This is a manual reset event.  For more

+    // information on events, see the documentation for

+    // CreateEvent() in the Windows SDK.

+    HANDLE m_hUnblockOutputPinEvent;

+

+    // This event will be signaled when block operation succeedes or

+    // when the user cancels the block operation.  The block operation

+    // can be canceled by calling IPinFlowControl2::Block( 0, NULL )

+    // while the block operation is pending.

+    HANDLE m_hNotifyCallerPinBlockedEvent;

+

+    // The state of the current block operation.

+    BLOCK_STATE m_BlockState;

+

+    // The ID of the thread which last called IPinFlowControl::Block().

+    // For more information on thread IDs, see the documentation for

+    // GetCurrentThreadID() in the Windows SDK.

+    DWORD m_dwBlockCallerThreadID;

+

+    // The number of times StartUsingOutputPin() has been sucessfully

+    // called and a corresponding call to StopUsingOutputPin() has not

+    // been made.  When this variable is greater than 0, the streaming

+    // thread is calling IPin::NewSegment(), IPin::EndOfStream(),

+    // IMemInputPin::Receive() or IMemInputPin::ReceiveMultiple().  The

+    // streaming thread could also be calling: DynamicReconnect(),

+    // ChangeMediaType() or ChangeOutputFormat().  The output pin cannot

+    // be blocked while the output pin is being used.

+    DWORD m_dwNumOutstandingOutputPinUsers;

+

+    // This event should be set when the IMediaFilter::Stop() is called.

+    // This is a manual reset event.  It is also set when the output pin

+    // delivers a flush to the connected input pin.

+    HANDLE m_hStopEvent;

+    IGraphConfig* m_pGraphConfig;

+

+    // TRUE if the output pin's allocator's samples are read only.

+    // Otherwise FALSE.  For more information, see the documentation

+    // for IMemInputPin::NotifyAllocator().

+    BOOL m_bPinUsesReadOnlyAllocator;

+

+private:

+    HRESULT Initialize(void);

+    HRESULT ChangeMediaTypeHelper(const CMediaType *pmt);

+

+    #ifdef DEBUG

+    void AssertValid(void);

+    #endif // DEBUG

+};

+

+class CAutoUsingOutputPin

+{

+public:

+    CAutoUsingOutputPin( __in CDynamicOutputPin* pOutputPin, __inout HRESULT* phr );

+    ~CAutoUsingOutputPin();

+

+private:

+    CDynamicOutputPin* m_pOutputPin;

+};

+

+inline CAutoUsingOutputPin::CAutoUsingOutputPin( __in CDynamicOutputPin* pOutputPin, __inout HRESULT* phr ) :

+    m_pOutputPin(NULL)

+{

+    // The caller should always pass in valid pointers.

+    ASSERT( NULL != pOutputPin );

+    ASSERT( NULL != phr );

+

+    // Make sure the user initialized phr.

+    ASSERT( S_OK == *phr );

+

+    HRESULT hr = pOutputPin->StartUsingOutputPin();

+    if( FAILED( hr ) )

+    {

+        *phr = hr;

+        return;

+    }

+

+    m_pOutputPin = pOutputPin;

+}

+

+inline CAutoUsingOutputPin::~CAutoUsingOutputPin()

+{

+    if( NULL != m_pOutputPin )

+    {

+        m_pOutputPin->StopUsingOutputPin();

+    }

+}

+

+#ifdef DEBUG

+

+inline HRESULT CDynamicOutputPin::Deliver(IMediaSample *pSample)

+{

+    // The caller should call StartUsingOutputPin() before calling this

+    // method.

+    ASSERT(StreamingThreadUsingOutputPin());

+

+    return CBaseOutputPin::Deliver(pSample);

+}

+

+inline HRESULT CDynamicOutputPin::DeliverEndOfStream(void)

+{

+    // The caller should call StartUsingOutputPin() before calling this

+    // method.

+    ASSERT( StreamingThreadUsingOutputPin() );

+

+    return CBaseOutputPin::DeliverEndOfStream();

+}

+

+inline HRESULT CDynamicOutputPin::DeliverNewSegment(REFERENCE_TIME tStart, REFERENCE_TIME tStop, double dRate)

+{

+    // The caller should call StartUsingOutputPin() before calling this

+    // method.

+    ASSERT(StreamingThreadUsingOutputPin());

+

+    return CBaseOutputPin::DeliverNewSegment(tStart, tStop, dRate);

+}

+

+#endif // DEBUG

+

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

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

+// Memory allocators

+//

+// the shared memory transport between pins requires the input pin

+// to provide a memory allocator that can provide sample objects. A

+// sample object supports the IMediaSample interface.

+//

+// CBaseAllocator handles the management of free and busy samples. It

+// allocates CMediaSample objects. CBaseAllocator is an abstract class:

+// in particular it has no method of initializing the list of free

+// samples. CMemAllocator is derived from CBaseAllocator and initializes

+// the list of samples using memory from the standard IMalloc interface.

+//

+// If you want your buffers to live in some special area of memory,

+// derive your allocator object from CBaseAllocator. If you derive your

+// IMemInputPin interface object from CBaseMemInputPin, you will get

+// CMemAllocator-based allocation etc for free and will just need to

+// supply the Receive handling, and media type / format negotiation.

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

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

+

+

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

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

+// Defines CMediaSample

+//

+// an object of this class supports IMediaSample and represents a buffer

+// for media data with some associated properties. Releasing it returns

+// it to a freelist managed by a CBaseAllocator derived object.

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

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

+

+class CMediaSample : public IMediaSample2    // The interface we support

+{

+

+protected:

+

+    friend class CBaseAllocator;

+

+    /*  Values for dwFlags - these are used for backward compatiblity

+        only now - use AM_SAMPLE_xxx

+    */

+    enum { Sample_SyncPoint       = 0x01,   /* Is this a sync point */

+           Sample_Preroll         = 0x02,   /* Is this a preroll sample */

+           Sample_Discontinuity   = 0x04,   /* Set if start of new segment */

+           Sample_TypeChanged     = 0x08,   /* Has the type changed */

+           Sample_TimeValid       = 0x10,   /* Set if time is valid */

+           Sample_MediaTimeValid  = 0x20,   /* Is the media time valid */

+           Sample_TimeDiscontinuity = 0x40, /* Time discontinuity */

+           Sample_StopValid       = 0x100,  /* Stop time valid */

+           Sample_ValidFlags      = 0x1FF

+         };

+

+    /* Properties, the media sample class can be a container for a format

+       change in which case we take a copy of a type through the SetMediaType

+       interface function and then return it when GetMediaType is called. As

+       we do no internal processing on it we leave it as a pointer */

+

+    DWORD            m_dwFlags;         /* Flags for this sample */

+                                        /* Type specific flags are packed

+                                           into the top word

+                                        */

+    DWORD            m_dwTypeSpecificFlags; /* Media type specific flags */

+    __field_ecount_opt(m_cbBuffer) LPBYTE           m_pBuffer;         /* Pointer to the complete buffer */

+    LONG             m_lActual;         /* Length of data in this sample */

+    LONG             m_cbBuffer;        /* Size of the buffer */

+    CBaseAllocator  *m_pAllocator;      /* The allocator who owns us */

+    CMediaSample     *m_pNext;          /* Chaining in free list */

+    REFERENCE_TIME   m_Start;           /* Start sample time */

+    REFERENCE_TIME   m_End;             /* End sample time */

+    LONGLONG         m_MediaStart;      /* Real media start position */

+    LONG             m_MediaEnd;        /* A difference to get the end */

+    AM_MEDIA_TYPE    *m_pMediaType;     /* Media type change data */

+    DWORD            m_dwStreamId;      /* Stream id */

+public:

+    LONG             m_cRef;            /* Reference count */

+

+

+public:

+

+    CMediaSample(

+        __in_opt LPCTSTR pName,

+        __in_opt CBaseAllocator *pAllocator,

+        __inout_opt HRESULT *phr,

+        __in_bcount_opt(length) LPBYTE pBuffer = NULL,

+        LONG length = 0);

+#ifdef UNICODE

+    CMediaSample(

+        __in_opt LPCSTR pName,

+        __in_opt CBaseAllocator *pAllocator,

+        __inout_opt HRESULT *phr,

+        __in_bcount_opt(length) LPBYTE pBuffer = NULL,

+        LONG length = 0);

+#endif

+

+    virtual ~CMediaSample();

+

+    /* Note the media sample does not delegate to its owner */

+

+    STDMETHODIMP QueryInterface(REFIID riid, __deref_out void **ppv);

+    STDMETHODIMP_(ULONG) AddRef();

+    STDMETHODIMP_(ULONG) Release();

+

+    // 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 SetPointer(__in_bcount(cBytes) BYTE * ptr, LONG cBytes);

+

+    // Get me a read/write pointer to this buffer's memory.

+    STDMETHODIMP GetPointer(__deref_out BYTE ** ppBuffer);

+

+    STDMETHODIMP_(LONG) GetSize(void);

+

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

+    STDMETHODIMP GetTime(

+        __out REFERENCE_TIME * pTimeStart,     // put time here

+        __out REFERENCE_TIME * pTimeEnd

+    );

+

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

+    STDMETHODIMP SetTime(

+        __in_opt REFERENCE_TIME * pTimeStart,     // put time here

+        __in_opt REFERENCE_TIME * pTimeEnd

+    );

+    STDMETHODIMP IsSyncPoint(void);

+    STDMETHODIMP SetSyncPoint(BOOL bIsSyncPoint);

+    STDMETHODIMP IsPreroll(void);

+    STDMETHODIMP SetPreroll(BOOL bIsPreroll);

+

+    STDMETHODIMP_(LONG) GetActualDataLength(void);

+    STDMETHODIMP SetActualDataLength(LONG lActual);

+

+    // these allow for limited format changes in band

+

+    STDMETHODIMP GetMediaType(__deref_out AM_MEDIA_TYPE **ppMediaType);

+    STDMETHODIMP SetMediaType(__in_opt AM_MEDIA_TYPE *pMediaType);

+

+    // 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 IsDiscontinuity(void);

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

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

+    STDMETHODIMP SetDiscontinuity(BOOL bDiscontinuity);

+

+    // get the media times for this sample

+    STDMETHODIMP GetMediaTime(

+    	__out LONGLONG * pTimeStart,

+	    __out LONGLONG * pTimeEnd

+    );

+

+    // Set the media times for this sample

+    STDMETHODIMP SetMediaTime(

+    	__in_opt LONGLONG * pTimeStart,

+	    __in_opt LONGLONG * pTimeEnd

+    );

+

+    // Set and get properties (IMediaSample2)

+    STDMETHODIMP GetProperties(

+        DWORD cbProperties,

+        __out_bcount(cbProperties) BYTE * pbProperties

+    );

+

+    STDMETHODIMP SetProperties(

+        DWORD cbProperties,

+        __in_bcount(cbProperties) const BYTE * pbProperties

+    );

+};

+

+

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

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

+// Defines CBaseAllocator

+//

+// Abstract base class that manages a list of media samples

+//

+// This class provides support for getting buffers from the free list,

+// including handling of commit and (asynchronous) decommit.

+//

+// Derive from this class and override the Alloc and Free functions to

+// allocate your CMediaSample (or derived) objects and add them to the

+// free list, preparing them as necessary.

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

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

+

+class AM_NOVTABLE CBaseAllocator : public CUnknown,// A non delegating IUnknown

+                       public IMemAllocatorCallbackTemp, // The interface we support

+                       public CCritSec             // Provides object locking

+{

+    class CSampleList;

+    friend class CSampleList;

+

+    /*  Trick to get at protected member in CMediaSample */

+    static CMediaSample * &NextSample(__in CMediaSample *pSample)

+    {

+        return pSample->m_pNext;

+    };

+

+    /*  Mini list class for the free list */

+    class CSampleList

+    {

+    public:

+        CSampleList() : m_List(NULL), m_nOnList(0) {};

+#ifdef DEBUG

+        ~CSampleList()

+        {

+            ASSERT(m_nOnList == 0);

+        };

+#endif

+        CMediaSample *Head() const { return m_List; };

+        CMediaSample *Next(__in CMediaSample *pSample) const { return CBaseAllocator::NextSample(pSample); };

+        int GetCount() const { return m_nOnList; };

+        void Add(__inout CMediaSample *pSample)

+        {

+            ASSERT(pSample != NULL);

+            CBaseAllocator::NextSample(pSample) = m_List;

+            m_List = pSample;

+            m_nOnList++;

+        };

+        CMediaSample *RemoveHead()

+        {

+            CMediaSample *pSample = m_List;

+            if (pSample != NULL) {

+                m_List = CBaseAllocator::NextSample(m_List);

+                m_nOnList--;

+            }

+            return pSample;

+        };

+        void Remove(__inout CMediaSample *pSample);

+

+    public:

+        CMediaSample *m_List;

+        int           m_nOnList;

+    };

+protected:

+

+    CSampleList m_lFree;        // Free list

+

+    /*  Note to overriders of CBaseAllocator.

+

+        We use a lazy signalling mechanism for waiting for samples.

+        This means we don't call the OS if no waits occur.

+

+        In order to implement this:

+

+        1. When a new sample is added to m_lFree call NotifySample() which

+           calls ReleaseSemaphore on m_hSem with a count of m_lWaiting and

+           sets m_lWaiting to 0.

+           This must all be done holding the allocator's critical section.

+

+        2. When waiting for a sample call SetWaiting() which increments

+           m_lWaiting BEFORE leaving the allocator's critical section.

+

+        3. Actually wait by calling WaitForSingleObject(m_hSem, INFINITE)

+           having left the allocator's critical section.  The effect of

+           this is to remove 1 from the semaphore's count.  You MUST call

+           this once having incremented m_lWaiting.

+

+        The following are then true when the critical section is not held :

+            (let nWaiting = number about to wait or waiting)

+

+            (1) if (m_lFree.GetCount() != 0) then (m_lWaiting == 0)

+            (2) m_lWaiting + Semaphore count == nWaiting

+

+        We would deadlock if

+           nWaiting != 0 &&

+           m_lFree.GetCount() != 0 &&

+           Semaphore count == 0

+

+           But from (1) if m_lFree.GetCount() != 0 then m_lWaiting == 0 so

+           from (2) Semaphore count == nWaiting (which is non-0) so the

+           deadlock can't happen.

+    */

+

+    HANDLE m_hSem;              // For signalling

+    long m_lWaiting;            // Waiting for a free element

+    long m_lCount;              // how many buffers we have agreed to provide

+    long m_lAllocated;          // how many buffers are currently allocated

+    long m_lSize;               // agreed size of each buffer

+    long m_lAlignment;          // agreed alignment

+    long m_lPrefix;             // agreed prefix (preceeds GetPointer() value)

+    BOOL m_bChanged;            // Have the buffer requirements changed

+

+    // if true, we are decommitted and can't allocate memory

+    BOOL m_bCommitted;

+    // if true, the decommit has happened, but we haven't called Free yet

+    // as there are still outstanding buffers

+    BOOL m_bDecommitInProgress;

+

+    //  Notification interface

+    IMemAllocatorNotifyCallbackTemp *m_pNotify;

+

+    BOOL m_fEnableReleaseCallback;

+

+    // called to decommit the memory when the last buffer is freed

+    // pure virtual - need to override this

+    virtual void Free(void) PURE;

+

+    // override to allocate the memory when commit called

+    virtual HRESULT Alloc(void);

+

+public:

+

+    CBaseAllocator(

+        __in_opt LPCTSTR , __inout_opt LPUNKNOWN, __inout HRESULT *,

+        BOOL bEvent = TRUE, BOOL fEnableReleaseCallback = FALSE);

+#ifdef UNICODE

+    CBaseAllocator(

+        __in_opt LPCSTR , __inout_opt LPUNKNOWN, __inout HRESULT *,

+        BOOL bEvent = TRUE, BOOL fEnableReleaseCallback = FALSE);

+#endif

+    virtual ~CBaseAllocator();

+

+    DECLARE_IUNKNOWN

+

+    // override this to publicise our interfaces

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

+

+    STDMETHODIMP SetProperties(

+		    __in ALLOCATOR_PROPERTIES* pRequest,

+		    __out ALLOCATOR_PROPERTIES* pActual);

+

+    // return the properties actually being used on this allocator

+    STDMETHODIMP GetProperties(

+		    __out ALLOCATOR_PROPERTIES* pProps);

+

+    // override Commit to allocate memory. We handle the GetBuffer

+    //state changes

+    STDMETHODIMP Commit();

+

+    // override this to handle the memory freeing. We handle any outstanding

+    // GetBuffer calls

+    STDMETHODIMP Decommit();

+

+    // 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. The two time parameters are

+    // optional and either may be NULL, they may alternatively be set to

+    // the start and end times the sample will have attached to it

+    // bPrevFramesSkipped is not used (used only by the video renderer's

+    // allocator where it affects quality management in direct draw).

+

+    STDMETHODIMP GetBuffer(__deref_out IMediaSample **ppBuffer,

+                           __in_opt REFERENCE_TIME * pStartTime,

+                           __in_opt REFERENCE_TIME * pEndTime,

+                           DWORD dwFlags);

+

+    // final release of a CMediaSample will call this

+    STDMETHODIMP ReleaseBuffer(IMediaSample *pBuffer);

+    // obsolete:: virtual void PutOnFreeList(CMediaSample * pSample);

+

+    STDMETHODIMP SetNotify(IMemAllocatorNotifyCallbackTemp *pNotify);

+

+    STDMETHODIMP GetFreeCount(__out LONG *plBuffersFree);

+

+    // Notify that a sample is available

+    void NotifySample();

+

+    // Notify that we're waiting for a sample

+    void SetWaiting() { m_lWaiting++; };

+};

+

+

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

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

+// Defines CMemAllocator

+//

+// this is an allocator based on CBaseAllocator that allocates sample

+// buffers in main memory (from 'new'). You must call SetProperties

+// before calling Commit.

+//

+// we don't free the memory when going into Decommit state. The simplest

+// way to implement this without complicating CBaseAllocator is to

+// have a Free() function, called to go into decommit state, that does

+// nothing and a ReallyFree function called from our destructor that

+// actually frees the memory.

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

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

+

+//  Make me one from quartz.dll

+STDAPI CreateMemoryAllocator(__deref_out IMemAllocator **ppAllocator);

+

+class CMemAllocator : public CBaseAllocator

+{

+

+protected:

+

+    LPBYTE m_pBuffer;   // combined memory for all buffers

+

+    // override to free the memory when decommit completes

+    // - we actually do nothing, and save the memory until deletion.

+    void Free(void);

+

+    // called from the destructor (and from Alloc if changing size/count) to

+    // actually free up the memory

+    void ReallyFree(void);

+

+    // overriden to allocate the memory when commit called

+    HRESULT Alloc(void);

+

+public:

+    /* This goes in the factory template table to create new instances */

+    static CUnknown *CreateInstance(__inout_opt LPUNKNOWN, __inout HRESULT *);

+

+    STDMETHODIMP SetProperties(

+		    __in ALLOCATOR_PROPERTIES* pRequest,

+		    __out ALLOCATOR_PROPERTIES* pActual);

+

+    CMemAllocator(__in_opt LPCTSTR , __inout_opt LPUNKNOWN, __inout HRESULT *);

+#ifdef UNICODE

+    CMemAllocator(__in_opt LPCSTR , __inout_opt LPUNKNOWN, __inout HRESULT *);

+#endif

+    ~CMemAllocator();

+};

+

+// helper used by IAMovieSetup implementation

+STDAPI

+AMovieSetupRegisterFilter( const AMOVIESETUP_FILTER * const psetupdata

+                         , IFilterMapper *                  pIFM

+                         , BOOL                             bRegister  );

+

+

+///////////////////////////////////////////////////////////////////////////

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

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

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

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

+///////////////////////////////////////////////////////////////////////////

+

+#endif /* __FILTER__ */

+

+

+

diff --git a/jni/pjproject-android/third_party/BaseClasses/amvideo.cpp b/jni/pjproject-android/third_party/BaseClasses/amvideo.cpp
new file mode 100644
index 0000000..ad1436f
--- /dev/null
+++ b/jni/pjproject-android/third_party/BaseClasses/amvideo.cpp
@@ -0,0 +1,280 @@
+//------------------------------------------------------------------------------

+// File: AMVideo.cpp

+//

+// Desc: DirectShow base classes - implements helper functions for

+//       bitmap formats.

+//

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

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

+

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

+

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

+

+#include <streams.h>

+#include <limits.h>

+

+// These are bit field masks for true colour devices

+

+const DWORD bits555[] = {0x007C00,0x0003E0,0x00001F};

+const DWORD bits565[] = {0x00F800,0x0007E0,0x00001F};

+const DWORD bits888[] = {0xFF0000,0x00FF00,0x0000FF};

+

+// This maps bitmap subtypes into a bits per pixel value and also a

+// name. unicode and ansi versions are stored because we have to

+// return a pointer to a static string.

+const struct {

+    const GUID *pSubtype;

+    WORD BitCount;

+    CHAR *pName;

+    WCHAR *wszName;

+} BitCountMap[] =  { &MEDIASUBTYPE_RGB1,        1,   "RGB Monochrome",     L"RGB Monochrome",   

+                     &MEDIASUBTYPE_RGB4,        4,   "RGB VGA",            L"RGB VGA",          

+                     &MEDIASUBTYPE_RGB8,        8,   "RGB 8",              L"RGB 8",            

+                     &MEDIASUBTYPE_RGB565,      16,  "RGB 565 (16 bit)",   L"RGB 565 (16 bit)", 

+                     &MEDIASUBTYPE_RGB555,      16,  "RGB 555 (16 bit)",   L"RGB 555 (16 bit)", 

+                     &MEDIASUBTYPE_RGB24,       24,  "RGB 24",             L"RGB 24",           

+                     &MEDIASUBTYPE_RGB32,       32,  "RGB 32",             L"RGB 32",

+                     &MEDIASUBTYPE_ARGB32,    32,  "ARGB 32",             L"ARGB 32",

+                     &MEDIASUBTYPE_Overlay,     0,   "Overlay",            L"Overlay",          

+                     &GUID_NULL,                0,   "UNKNOWN",            L"UNKNOWN"           

+};

+

+// Return the size of the bitmap as defined by this header

+

+STDAPI_(DWORD) GetBitmapSize(const BITMAPINFOHEADER *pHeader)

+{

+    return DIBSIZE(*pHeader);

+}

+

+

+// This is called if the header has a 16 bit colour depth and needs to work

+// out the detailed type from the bit fields (either RGB 565 or RGB 555)

+

+STDAPI_(const GUID) GetTrueColorType(const BITMAPINFOHEADER *pbmiHeader)

+{

+    BITMAPINFO *pbmInfo = (BITMAPINFO *) pbmiHeader;

+    ASSERT(pbmiHeader->biBitCount == 16);

+

+    // If its BI_RGB then it's RGB 555 by default

+

+    if (pbmiHeader->biCompression == BI_RGB) {

+        return MEDIASUBTYPE_RGB555;

+    }

+

+    // Compare the bit fields with RGB 555

+

+    DWORD *pMask = (DWORD *) pbmInfo->bmiColors;

+    if (pMask[0] == bits555[0]) {

+        if (pMask[1] == bits555[1]) {

+            if (pMask[2] == bits555[2]) {

+                return MEDIASUBTYPE_RGB555;

+            }

+        }

+    }

+

+    // Compare the bit fields with RGB 565

+

+    pMask = (DWORD *) pbmInfo->bmiColors;

+    if (pMask[0] == bits565[0]) {

+        if (pMask[1] == bits565[1]) {

+            if (pMask[2] == bits565[2]) {

+                return MEDIASUBTYPE_RGB565;

+            }

+        }

+    }

+    return GUID_NULL;

+}

+

+

+// Given a BITMAPINFOHEADER structure this returns the GUID sub type that is

+// used to describe it in format negotiations. For example a video codec fills

+// in the format block with a VIDEOINFO structure, it also fills in the major

+// type with MEDIATYPE_VIDEO and the subtype with a GUID that matches the bit

+// count, for example if it is an eight bit image then MEDIASUBTYPE_RGB8

+

+STDAPI_(const GUID) GetBitmapSubtype(const BITMAPINFOHEADER *pbmiHeader)

+{

+    ASSERT(pbmiHeader);

+

+    // If it's not RGB then create a GUID from the compression type

+

+    if (pbmiHeader->biCompression != BI_RGB) {

+        if (pbmiHeader->biCompression != BI_BITFIELDS) {

+            FOURCCMap FourCCMap(pbmiHeader->biCompression);

+            return (const GUID) FourCCMap;

+        }

+    }

+

+    // Map the RGB DIB bit depth to a image GUID

+

+    switch(pbmiHeader->biBitCount) {

+        case 1    :   return MEDIASUBTYPE_RGB1;

+        case 4    :   return MEDIASUBTYPE_RGB4;

+        case 8    :   return MEDIASUBTYPE_RGB8;

+        case 16   :   return GetTrueColorType(pbmiHeader);

+        case 24   :   return MEDIASUBTYPE_RGB24;

+        case 32   :   return MEDIASUBTYPE_RGB32;

+    }

+    return GUID_NULL;

+}

+

+

+// Given a video bitmap subtype we return the number of bits per pixel it uses

+// We return a WORD bit count as thats what the BITMAPINFOHEADER uses. If the

+// GUID subtype is not found in the table we return an invalid USHRT_MAX

+

+STDAPI_(WORD) GetBitCount(const GUID *pSubtype)

+{

+    ASSERT(pSubtype);

+    const GUID *pMediaSubtype;

+    INT iPosition = 0;

+

+    // Scan the mapping list seeing if the source GUID matches any known

+    // bitmap subtypes, the list is terminated by a GUID_NULL entry

+

+    while (TRUE) {

+        pMediaSubtype = BitCountMap[iPosition].pSubtype;

+        if (IsEqualGUID(*pMediaSubtype,GUID_NULL)) {

+            return USHRT_MAX;

+        }

+        if (IsEqualGUID(*pMediaSubtype,*pSubtype)) {

+            return BitCountMap[iPosition].BitCount;

+        }

+        iPosition++;

+    }

+}

+

+

+// Given a bitmap subtype we return a description name that can be used for

+// debug purposes. In a retail build this function still returns the names

+// If the subtype isn't found in the lookup table we return string UNKNOWN

+

+int LocateSubtype(const GUID *pSubtype)

+{

+    ASSERT(pSubtype);

+    const GUID *pMediaSubtype;

+    INT iPosition = 0;

+

+    // Scan the mapping list seeing if the source GUID matches any known

+    // bitmap subtypes, the list is terminated by a GUID_NULL entry

+

+    while (TRUE) {

+        pMediaSubtype = BitCountMap[iPosition].pSubtype;

+        if (IsEqualGUID(*pMediaSubtype,*pSubtype) ||

+            IsEqualGUID(*pMediaSubtype,GUID_NULL)

+            )

+        {

+            break;

+        }

+        

+        iPosition++;

+    }

+

+    return iPosition;

+}

+

+

+

+STDAPI_(WCHAR *) GetSubtypeNameW(const GUID *pSubtype)

+{

+    return BitCountMap[LocateSubtype(pSubtype)].wszName;

+}

+

+STDAPI_(CHAR *) GetSubtypeNameA(const GUID *pSubtype)

+{

+    return BitCountMap[LocateSubtype(pSubtype)].pName;

+}

+

+#ifndef GetSubtypeName

+#error wxutil.h should have defined GetSubtypeName

+#endif

+#undef GetSubtypeName

+

+// this is here for people that linked to it directly; most people

+// would use the header file that picks the A or W version.

+STDAPI_(CHAR *) GetSubtypeName(const GUID *pSubtype)

+{

+    return GetSubtypeNameA(pSubtype);

+}

+

+

+// The mechanism for describing a bitmap format is with the BITMAPINFOHEADER

+// This is really messy to deal with because it invariably has fields that

+// follow it holding bit fields, palettes and the rest. This function gives

+// the number of bytes required to hold a VIDEOINFO that represents it. This

+// count includes the prefix information (like the rcSource rectangle) the

+// BITMAPINFOHEADER field, and any other colour information on the end.

+//

+// WARNING If you want to copy a BITMAPINFOHEADER into a VIDEOINFO always make

+// sure that you use the HEADER macro because the BITMAPINFOHEADER field isn't

+// right at the start of the VIDEOINFO (there are a number of other fields),

+//

+//     CopyMemory(HEADER(pVideoInfo),pbmi,sizeof(BITMAPINFOHEADER));

+//

+

+STDAPI_(LONG) GetBitmapFormatSize(const BITMAPINFOHEADER *pHeader)

+{

+    // Everyone has this to start with this  

+    LONG Size = SIZE_PREHEADER + pHeader->biSize;

+

+    ASSERT(pHeader->biSize >= sizeof(BITMAPINFOHEADER));

+    

+    // Does this format use a palette, if the number of colours actually used

+    // is zero then it is set to the maximum that are allowed for that colour

+    // depth (an example is 256 for eight bits). Truecolour formats may also

+    // pass a palette with them in which case the used count is non zero

+

+    // This would scare me.

+    ASSERT(pHeader->biBitCount <= iPALETTE || pHeader->biClrUsed == 0);

+

+    if (pHeader->biBitCount <= iPALETTE || pHeader->biClrUsed) {

+        LONG Entries = (DWORD) 1 << pHeader->biBitCount;

+        if (pHeader->biClrUsed) {

+            Entries = pHeader->biClrUsed;

+        }

+        Size += Entries * sizeof(RGBQUAD);

+    }

+

+    // Truecolour formats may have a BI_BITFIELDS specifier for compression

+    // type which means that room for three DWORDs should be allocated that

+    // specify where in each pixel the RGB colour components may be found

+

+    if (pHeader->biCompression == BI_BITFIELDS) {

+        Size += SIZE_MASKS;

+    }

+

+    // A BITMAPINFO for a palettised image may also contain a palette map that

+    // provides the information to map from a source palette to a destination

+    // palette during a BitBlt for example, because this information is only

+    // ever processed during drawing you don't normally store the palette map

+    // nor have any way of knowing if it is present in the data structure

+

+    return Size;

+}

+

+

+// Returns TRUE if the VIDEOINFO contains a palette

+

+STDAPI_(BOOL) ContainsPalette(const VIDEOINFOHEADER *pVideoInfo)

+{

+    if (PALETTISED(pVideoInfo) == FALSE) {

+        if (pVideoInfo->bmiHeader.biClrUsed == 0) {

+            return FALSE;

+        }

+    }

+    return TRUE;

+}

+

+

+// Return a pointer to the first entry in a palette

+

+STDAPI_(const RGBQUAD *) GetBitmapPalette(const VIDEOINFOHEADER *pVideoInfo)

+{

+    if (pVideoInfo->bmiHeader.biCompression == BI_BITFIELDS) {

+        return TRUECOLOR(pVideoInfo)->bmiColors;

+    }

+    return COLORS(pVideoInfo);

+}

+

+#endif /* PJMEDIA_VIDEO_DEV_HAS_DSHOW */

diff --git a/jni/pjproject-android/third_party/BaseClasses/arithutil.cpp b/jni/pjproject-android/third_party/BaseClasses/arithutil.cpp
new file mode 100644
index 0000000..9600e5f
--- /dev/null
+++ b/jni/pjproject-android/third_party/BaseClasses/arithutil.cpp
@@ -0,0 +1,366 @@
+//------------------------------------------------------------------------------

+// File: ArithUtil.cpp

+//

+// Desc: DirectShow base classes - implements helper classes for building

+//       multimedia filters.

+//

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

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

+

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

+

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

+

+#include <streams.h>

+

+//

+//  Declare function from largeint.h we need so that PPC can build

+//

+

+//

+// Enlarged integer divide - 64-bits / 32-bits > 32-bits

+//

+

+#ifndef _X86_

+

+#define LLtoU64(x) (*(unsigned __int64*)(void*)(&(x)))

+

+__inline

+ULONG

+WINAPI

+EnlargedUnsignedDivide (

+    IN ULARGE_INTEGER Dividend,

+    IN ULONG Divisor,

+    IN PULONG Remainder

+    )

+{

+        // return remainder if necessary

+        if (Remainder != NULL)

+                *Remainder = (ULONG)(LLtoU64(Dividend) % Divisor);

+        return (ULONG)(LLtoU64(Dividend) / Divisor);

+}

+

+#else

+__inline

+ULONG

+WINAPI

+EnlargedUnsignedDivide (

+    IN ULARGE_INTEGER Dividend,

+    IN ULONG Divisor,

+    IN PULONG Remainder

+    )

+{

+    ULONG ulResult;

+    _asm {

+        mov eax,Dividend.LowPart

+        mov edx,Dividend.HighPart

+        mov ecx,Remainder

+        div Divisor

+        or  ecx,ecx

+        jz  short label

+        mov [ecx],edx

+label:

+        mov ulResult,eax

+    }

+    return ulResult;

+}

+#endif

+

+

+/*  Arithmetic functions to help with time format conversions

+*/

+

+#ifdef _M_ALPHA

+// work around bug in version 12.00.8385 of the alpha compiler where

+// UInt32x32To64 sign-extends its arguments (?)

+#undef UInt32x32To64

+#define UInt32x32To64(a, b) (((ULONGLONG)((ULONG)(a)) & 0xffffffff) * ((ULONGLONG)((ULONG)(b)) & 0xffffffff))

+#endif

+

+/*   Compute (a * b + d) / c */

+LONGLONG WINAPI llMulDiv(LONGLONG a, LONGLONG b, LONGLONG c, LONGLONG d)

+{

+    /*  Compute the absolute values to avoid signed arithmetic problems */

+    ULARGE_INTEGER ua, ub;

+    DWORDLONG uc;

+

+    ua.QuadPart = (DWORDLONG)(a >= 0 ? a : -a);

+    ub.QuadPart = (DWORDLONG)(b >= 0 ? b : -b);

+    uc          = (DWORDLONG)(c >= 0 ? c : -c);

+    BOOL bSign = (a < 0) ^ (b < 0);

+

+    /*  Do long multiplication */

+    ULARGE_INTEGER p[2];

+    p[0].QuadPart  = UInt32x32To64(ua.LowPart, ub.LowPart);

+

+    /*  This next computation cannot overflow into p[1].HighPart because

+        the max number we can compute here is:

+

+                 (2 ** 32 - 1) * (2 ** 32 - 1) +  // ua.LowPart * ub.LowPart

+    (2 ** 32) *  (2 ** 31) * (2 ** 32 - 1) * 2    // x.LowPart * y.HighPart * 2

+

+    == 2 ** 96 - 2 ** 64 + (2 ** 64 - 2 ** 33 + 1)

+    == 2 ** 96 - 2 ** 33 + 1

+    < 2 ** 96

+    */

+

+    ULARGE_INTEGER x;

+    x.QuadPart     = UInt32x32To64(ua.LowPart, ub.HighPart) +

+                     UInt32x32To64(ua.HighPart, ub.LowPart) +

+                     p[0].HighPart;

+    p[0].HighPart  = x.LowPart;

+    p[1].QuadPart  = UInt32x32To64(ua.HighPart, ub.HighPart) + x.HighPart;

+

+    if (d != 0) {

+        ULARGE_INTEGER ud[2];

+        if (bSign) {

+            ud[0].QuadPart = (DWORDLONG)(-d);

+            if (d > 0) {

+                /*  -d < 0 */

+                ud[1].QuadPart = (DWORDLONG)(LONGLONG)-1;

+            } else {

+                ud[1].QuadPart = (DWORDLONG)0;

+            }

+        } else {

+            ud[0].QuadPart = (DWORDLONG)d;

+            if (d < 0) {

+                ud[1].QuadPart = (DWORDLONG)(LONGLONG)-1;

+            } else {

+                ud[1].QuadPart = (DWORDLONG)0;

+            }

+        }

+        /*  Now do extended addition */

+        ULARGE_INTEGER uliTotal;

+

+        /*  Add ls DWORDs */

+        uliTotal.QuadPart  = (DWORDLONG)ud[0].LowPart + p[0].LowPart;

+        p[0].LowPart       = uliTotal.LowPart;

+

+        /*  Propagate carry */

+        uliTotal.LowPart   = uliTotal.HighPart;

+        uliTotal.HighPart  = 0;

+

+        /*  Add 2nd most ls DWORDs */

+        uliTotal.QuadPart += (DWORDLONG)ud[0].HighPart + p[0].HighPart;

+        p[0].HighPart      = uliTotal.LowPart;

+

+        /*  Propagate carry */

+        uliTotal.LowPart   = uliTotal.HighPart;

+        uliTotal.HighPart  = 0;

+

+        /*  Add MS DWORDLONGs - no carry expected */

+        p[1].QuadPart     += ud[1].QuadPart + uliTotal.QuadPart;

+

+        /*  Now see if we got a sign change from the addition */

+        if ((LONG)p[1].HighPart < 0) {

+            bSign = !bSign;

+

+            /*  Negate the current value (ugh!) */

+            p[0].QuadPart  = ~p[0].QuadPart;

+            p[1].QuadPart  = ~p[1].QuadPart;

+            p[0].QuadPart += 1;

+            p[1].QuadPart += (p[0].QuadPart == 0);

+        }

+    }

+

+    /*  Now for the division */

+    if (c < 0) {

+        bSign = !bSign;

+    }

+

+

+    /*  This will catch c == 0 and overflow */

+    if (uc <= p[1].QuadPart) {

+        return bSign ? (LONGLONG)0x8000000000000000 :

+                       (LONGLONG)0x7FFFFFFFFFFFFFFF;

+    }

+

+    DWORDLONG ullResult;

+

+    /*  Do the division */

+    /*  If the dividend is a DWORD_LONG use the compiler */

+    if (p[1].QuadPart == 0) {

+        ullResult = p[0].QuadPart / uc;

+        return bSign ? -(LONGLONG)ullResult : (LONGLONG)ullResult;

+    }

+

+    /*  If the divisor is a DWORD then its simpler */

+    ULARGE_INTEGER ulic;

+    ulic.QuadPart = uc;

+    if (ulic.HighPart == 0) {

+        ULARGE_INTEGER uliDividend;

+        ULARGE_INTEGER uliResult;

+        DWORD dwDivisor = (DWORD)uc;

+        // ASSERT(p[1].HighPart == 0 && p[1].LowPart < dwDivisor);

+        uliDividend.HighPart = p[1].LowPart;

+        uliDividend.LowPart = p[0].HighPart;

+#ifndef USE_LARGEINT

+        uliResult.HighPart = (DWORD)(uliDividend.QuadPart / dwDivisor);

+        p[0].HighPart = (DWORD)(uliDividend.QuadPart % dwDivisor);

+        uliResult.LowPart = 0;

+        uliResult.QuadPart = p[0].QuadPart / dwDivisor + uliResult.QuadPart;

+#else

+        /*  NOTE - this routine will take exceptions if

+            the result does not fit in a DWORD

+        */

+        if (uliDividend.QuadPart >= (DWORDLONG)dwDivisor) {

+            uliResult.HighPart = EnlargedUnsignedDivide(

+                                     uliDividend,

+                                     dwDivisor,

+                                     &p[0].HighPart);

+        } else {

+            uliResult.HighPart = 0;

+        }

+        uliResult.LowPart = EnlargedUnsignedDivide(

+                                 p[0],

+                                 dwDivisor,

+                                 NULL);

+#endif

+        return bSign ? -(LONGLONG)uliResult.QuadPart :

+                        (LONGLONG)uliResult.QuadPart;

+    }

+

+

+    ullResult = 0;

+

+    /*  OK - do long division */

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

+        ullResult <<= 1;

+

+        /*  Shift 128 bit p left 1 */

+        p[1].QuadPart <<= 1;

+        if ((p[0].HighPart & 0x80000000) != 0) {

+            p[1].LowPart++;

+        }

+        p[0].QuadPart <<= 1;

+

+        /*  Compare */

+        if (uc <= p[1].QuadPart) {

+            p[1].QuadPart -= uc;

+            ullResult += 1;

+        }

+    }

+

+    return bSign ? - (LONGLONG)ullResult : (LONGLONG)ullResult;

+}

+

+LONGLONG WINAPI Int64x32Div32(LONGLONG a, LONG b, LONG c, LONG d)

+{

+    ULARGE_INTEGER ua;

+    DWORD ub;

+    DWORD uc;

+

+    /*  Compute the absolute values to avoid signed arithmetic problems */

+    ua.QuadPart = (DWORDLONG)(a >= 0 ? a : -a);

+    ub = (DWORD)(b >= 0 ? b : -b);

+    uc = (DWORD)(c >= 0 ? c : -c);

+    BOOL bSign = (a < 0) ^ (b < 0);

+

+    /*  Do long multiplication */

+    ULARGE_INTEGER p0;

+    DWORD p1;

+    p0.QuadPart  = UInt32x32To64(ua.LowPart, ub);

+

+    if (ua.HighPart != 0) {

+        ULARGE_INTEGER x;

+        x.QuadPart     = UInt32x32To64(ua.HighPart, ub) + p0.HighPart;

+        p0.HighPart  = x.LowPart;

+        p1   = x.HighPart;

+    } else {

+        p1 = 0;

+    }

+

+    if (d != 0) {

+        ULARGE_INTEGER ud0;

+        DWORD ud1;

+

+        if (bSign) {

+            //

+            //  Cast d to LONGLONG first otherwise -0x80000000 sign extends

+            //  incorrectly

+            //

+            ud0.QuadPart = (DWORDLONG)(-(LONGLONG)d);

+            if (d > 0) {

+                /*  -d < 0 */

+                ud1 = (DWORD)-1;

+            } else {

+                ud1 = (DWORD)0;

+            }

+        } else {

+            ud0.QuadPart = (DWORDLONG)d;

+            if (d < 0) {

+                ud1 = (DWORD)-1;

+            } else {

+                ud1 = (DWORD)0;

+            }

+        }

+        /*  Now do extended addition */

+        ULARGE_INTEGER uliTotal;

+

+        /*  Add ls DWORDs */

+        uliTotal.QuadPart  = (DWORDLONG)ud0.LowPart + p0.LowPart;

+        p0.LowPart       = uliTotal.LowPart;

+

+        /*  Propagate carry */

+        uliTotal.LowPart   = uliTotal.HighPart;

+        uliTotal.HighPart  = 0;

+

+        /*  Add 2nd most ls DWORDs */

+        uliTotal.QuadPart += (DWORDLONG)ud0.HighPart + p0.HighPart;

+        p0.HighPart      = uliTotal.LowPart;

+

+        /*  Add MS DWORDLONGs - no carry expected */

+        p1 += ud1 + uliTotal.HighPart;

+

+        /*  Now see if we got a sign change from the addition */

+        if ((LONG)p1 < 0) {

+            bSign = !bSign;

+

+            /*  Negate the current value (ugh!) */

+            p0.QuadPart  = ~p0.QuadPart;

+            p1 = ~p1;

+            p0.QuadPart += 1;

+            p1 += (p0.QuadPart == 0);

+        }

+    }

+

+    /*  Now for the division */

+    if (c < 0) {

+        bSign = !bSign;

+    }

+

+

+    /*  This will catch c == 0 and overflow */

+    if (uc <= p1) {

+        return bSign ? (LONGLONG)0x8000000000000000 :

+                       (LONGLONG)0x7FFFFFFFFFFFFFFF;

+    }

+

+    /*  Do the division */

+

+    /*  If the divisor is a DWORD then its simpler */

+    ULARGE_INTEGER uliDividend;

+    ULARGE_INTEGER uliResult;

+    DWORD dwDivisor = uc;

+    uliDividend.HighPart = p1;

+    uliDividend.LowPart = p0.HighPart;

+    /*  NOTE - this routine will take exceptions if

+        the result does not fit in a DWORD

+    */

+    if (uliDividend.QuadPart >= (DWORDLONG)dwDivisor) {

+        uliResult.HighPart = EnlargedUnsignedDivide(

+                                 uliDividend,

+                                 dwDivisor,

+                                 &p0.HighPart);

+    } else {

+        uliResult.HighPart = 0;

+    }

+    uliResult.LowPart = EnlargedUnsignedDivide(

+                             p0,

+                             dwDivisor,

+                             NULL);

+    return bSign ? -(LONGLONG)uliResult.QuadPart :

+                    (LONGLONG)uliResult.QuadPart;

+}

+

+#endif /* PJMEDIA_VIDEO_DEV_HAS_DSHOW */

diff --git a/jni/pjproject-android/third_party/BaseClasses/combase.cpp b/jni/pjproject-android/third_party/BaseClasses/combase.cpp
new file mode 100644
index 0000000..bf7e351
--- /dev/null
+++ b/jni/pjproject-android/third_party/BaseClasses/combase.cpp
@@ -0,0 +1,269 @@
+//------------------------------------------------------------------------------

+// File: ComBase.cpp

+//

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

+//       COM objects.

+//

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

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

+

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

+

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

+

+#include <streams.h>

+#pragma warning( disable : 4514 )   // Disable warnings re unused inline functions

+

+

+/* Define the static member variable */

+

+LONG CBaseObject::m_cObjects = 0;

+

+

+/* Constructor */

+

+CBaseObject::CBaseObject(__in_opt LPCTSTR pName)

+{

+    /* Increment the number of active objects */

+    InterlockedIncrement(&m_cObjects);

+

+#ifdef DEBUG

+

+#ifdef UNICODE

+    m_dwCookie = DbgRegisterObjectCreation(0, pName);

+#else

+    m_dwCookie = DbgRegisterObjectCreation(pName, 0);

+#endif

+

+#endif

+}

+

+#ifdef UNICODE

+CBaseObject::CBaseObject(const char *pName)

+{

+    /* Increment the number of active objects */

+    InterlockedIncrement(&m_cObjects);

+

+#ifdef DEBUG

+    m_dwCookie = DbgRegisterObjectCreation(pName, 0);

+#endif

+}

+#endif

+

+HINSTANCE	hlibOLEAut32;

+

+/* Destructor */

+

+CBaseObject::~CBaseObject()

+{

+    /* Decrement the number of objects active */

+    if (InterlockedDecrement(&m_cObjects) == 0) {

+	if (hlibOLEAut32) {

+	    FreeLibrary(hlibOLEAut32);

+

+	    hlibOLEAut32 = 0;

+	}

+    };

+

+

+#ifdef DEBUG

+    DbgRegisterObjectDestruction(m_dwCookie);

+#endif

+}

+

+static const TCHAR szOle32Aut[]   = TEXT("OleAut32.dll");

+

+HINSTANCE LoadOLEAut32()

+{

+    if (hlibOLEAut32 == 0) {

+

+	hlibOLEAut32 = LoadLibrary(szOle32Aut);

+    }

+

+    return hlibOLEAut32;

+}

+

+

+/* Constructor */

+

+// We know we use "this" in the initialization list, we also know we don't modify *phr.

+#pragma warning( disable : 4355 4100 )

+CUnknown::CUnknown(__in_opt LPCTSTR pName, __in_opt LPUNKNOWN pUnk)

+: CBaseObject(pName)

+/* Start the object with a reference count of zero - when the      */

+/* object is queried for it's first interface this may be          */

+/* incremented depending on whether or not this object is          */

+/* currently being aggregated upon                                 */

+, m_cRef(0)

+/* Set our pointer to our IUnknown interface.                      */

+/* If we have an outer, use its, otherwise use ours.               */

+/* This pointer effectivly points to the owner of                  */

+/* this object and can be accessed by the GetOwner() method.       */

+, m_pUnknown( pUnk != 0 ? pUnk : reinterpret_cast<LPUNKNOWN>( static_cast<PNDUNKNOWN>(this) ) )

+ /* Why the double cast?  Well, the inner cast is a type-safe cast */

+ /* to pointer to a type from which we inherit.  The second is     */

+ /* type-unsafe but works because INonDelegatingUnknown "behaves   */

+ /* like" IUnknown. (Only the names on the methods change.)        */

+{

+    // Everything we need to do has been done in the initializer list

+}

+

+// This does the same as above except it has a useless HRESULT argument

+// use the previous constructor, this is just left for compatibility...

+CUnknown::CUnknown(__in_opt LPCTSTR pName, __in_opt LPUNKNOWN pUnk, __inout_opt HRESULT *phr) :

+    CBaseObject(pName),

+    m_cRef(0),

+    m_pUnknown( pUnk != 0 ? pUnk : reinterpret_cast<LPUNKNOWN>( static_cast<PNDUNKNOWN>(this) ) )

+{

+}

+

+#ifdef UNICODE

+CUnknown::CUnknown(__in_opt LPCSTR pName, __in_opt LPUNKNOWN pUnk)

+: CBaseObject(pName), m_cRef(0),

+    m_pUnknown( pUnk != 0 ? pUnk : reinterpret_cast<LPUNKNOWN>( static_cast<PNDUNKNOWN>(this) ) )

+{ }

+

+CUnknown::CUnknown(__in_opt LPCSTR pName, __in_opt LPUNKNOWN pUnk, __inout_opt HRESULT *phr) :

+    CBaseObject(pName), m_cRef(0),

+    m_pUnknown( pUnk != 0 ? pUnk : reinterpret_cast<LPUNKNOWN>( static_cast<PNDUNKNOWN>(this) ) )

+{ }

+

+#endif

+

+#pragma warning( default : 4355 4100 )

+

+

+/* QueryInterface */

+

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

+{

+    CheckPointer(ppv,E_POINTER);

+    ValidateReadWritePtr(ppv,sizeof(PVOID));

+

+    /* We know only about IUnknown */

+

+    if (riid == IID_IUnknown) {

+        GetInterface((LPUNKNOWN) (PNDUNKNOWN) this, ppv);

+        return NOERROR;

+    } else {

+        *ppv = NULL;

+        return E_NOINTERFACE;

+    }

+}

+

+/* We have to ensure that we DON'T use a max macro, since these will typically   */

+/* lead to one of the parameters being evaluated twice.  Since we are worried    */

+/* about concurrency, we can't afford to access the m_cRef twice since we can't  */

+/* afford to run the risk that its value having changed between accesses.        */

+

+template<class T> inline static T ourmax( const T & a, const T & b )

+{

+    return a > b ? a : b;

+}

+

+/* AddRef */

+

+STDMETHODIMP_(ULONG) CUnknown::NonDelegatingAddRef()

+{

+    LONG lRef = InterlockedIncrement( &m_cRef );

+    ASSERT(lRef > 0);

+    DbgLog((LOG_MEMORY,3,TEXT("    Obj %d ref++ = %d"),

+           m_dwCookie, m_cRef));

+    return ourmax(ULONG(m_cRef), 1ul);

+}

+

+

+/* Release */

+

+STDMETHODIMP_(ULONG) CUnknown::NonDelegatingRelease()

+{

+    /* If the reference count drops to zero delete ourselves */

+

+    LONG lRef = InterlockedDecrement( &m_cRef );

+    ASSERT(lRef >= 0);

+

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

+	    m_dwCookie, m_cRef));

+    if (lRef == 0) {

+

+        // COM rules say we must protect against re-entrancy.

+        // If we are an aggregator and we hold our own interfaces

+        // on the aggregatee, the QI for these interfaces will

+        // addref ourselves. So after doing the QI we must release

+        // a ref count on ourselves. Then, before releasing the

+        // private interface, we must addref ourselves. When we do

+        // this from the destructor here it will result in the ref

+        // count going to 1 and then back to 0 causing us to

+        // re-enter the destructor. Hence we add an extra refcount here

+        // once we know we will delete the object.

+        // for an example aggregator see filgraph\distrib.cpp.

+

+        m_cRef++;

+

+        delete this;

+        return ULONG(0);

+    } else {

+        //  Don't touch m_cRef again even in this leg as the object

+        //  may have just been released on another thread too

+        return ourmax(ULONG(lRef), 1ul);

+    }

+}

+

+

+/* Return an interface pointer to a requesting client

+   performing a thread safe AddRef as necessary */

+

+STDAPI GetInterface(LPUNKNOWN pUnk, __out void **ppv)

+{

+    CheckPointer(ppv, E_POINTER);

+    *ppv = pUnk;

+    pUnk->AddRef();

+    return NOERROR;

+}

+

+

+/* Compares two interfaces and returns TRUE if they are on the same object */

+

+BOOL WINAPI IsEqualObject(IUnknown *pFirst, IUnknown *pSecond)

+{

+    /*  Different objects can't have the same interface pointer for

+        any interface

+    */

+    if (pFirst == pSecond) {

+        return TRUE;

+    }

+    /*  OK - do it the hard way - check if they have the same

+        IUnknown pointers - a single object can only have one of these

+    */

+    LPUNKNOWN pUnknown1;     // Retrieve the IUnknown interface

+    LPUNKNOWN pUnknown2;     // Retrieve the other IUnknown interface

+    HRESULT hr;              // General OLE return code

+

+    ASSERT(pFirst);

+    ASSERT(pSecond);

+

+    /* See if the IUnknown pointers match */

+

+    hr = pFirst->QueryInterface(IID_IUnknown,(void **) &pUnknown1);

+    if (FAILED(hr)) {

+        return FALSE;

+    }

+    ASSERT(pUnknown1);

+

+    /* Release the extra interface we hold */

+

+    pUnknown1->Release();

+

+    hr = pSecond->QueryInterface(IID_IUnknown,(void **) &pUnknown2);

+    if (FAILED(hr)) {

+        return FALSE;

+    }

+    ASSERT(pUnknown2);

+

+    /* Release the extra interface we hold */

+

+    pUnknown2->Release();

+    return (pUnknown1 == pUnknown2);

+}

+

+#endif /* PJMEDIA_VIDEO_DEV_HAS_DSHOW */

diff --git a/jni/pjproject-android/third_party/BaseClasses/combase.h b/jni/pjproject-android/third_party/BaseClasses/combase.h
new file mode 100644
index 0000000..f735ba9
--- /dev/null
+++ b/jni/pjproject-android/third_party/BaseClasses/combase.h
@@ -0,0 +1,305 @@
+//------------------------------------------------------------------------------

+// File: ComBase.h

+//

+// Desc: DirectShow base classes - defines a class hierarchy for creating

+//       COM objects.

+//

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

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

+

+

+/*

+

+a. Derive your COM object from CUnknown

+

+b. Make a static CreateInstance function that takes an LPUNKNOWN, an HRESULT *

+   and a TCHAR *. The LPUNKNOWN defines the object to delegate IUnknown calls

+   to. The HRESULT * allows error codes to be passed around constructors and

+   the TCHAR * is a descriptive name that can be printed on the debugger.

+

+   It is important that constructors only change the HRESULT * if they have

+   to set an ERROR code, if it was successful then leave it alone or you may

+   overwrite an error code from an object previously created.

+

+   When you call a constructor the descriptive name should be in static store

+   as we do not copy the string. To stop large amounts of memory being used

+   in retail builds by all these static strings use the NAME macro,

+

+   CMyFilter = new CImplFilter(NAME("My filter"),pUnknown,phr);

+   if (FAILED(hr)) {

+       return hr;

+   }

+

+   In retail builds NAME(_x_) compiles to NULL, the base CBaseObject class

+   knows not to do anything with objects that don't have a name.

+

+c. Have a constructor for your object that passes the LPUNKNOWN, HRESULT * and

+   TCHAR * to the CUnknown constructor. You can set the HRESULT if you have an

+   error, or just simply pass it through to the constructor.

+

+   The object creation will fail in the class factory if the HRESULT indicates

+   an error (ie FAILED(HRESULT) == TRUE)

+

+d. Create a FactoryTemplate with your object's class id and CreateInstance

+   function.

+

+Then (for each interface) either

+

+Multiple inheritance

+

+1. Also derive it from ISomeInterface

+2. Include DECLARE_IUNKNOWN in your class definition to declare

+   implementations of QueryInterface, AddRef and Release that

+   call the outer unknown

+3. Override NonDelegatingQueryInterface to expose ISomeInterface by

+   code something like

+

+     if (riid == IID_ISomeInterface) {

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

+     } else {

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

+     }

+

+4. Declare and implement the member functions of ISomeInterface.

+

+or: Nested interfaces

+

+1. Declare a class derived from CUnknown

+2. Include DECLARE_IUNKNOWN in your class definition

+3. Override NonDelegatingQueryInterface to expose ISomeInterface by

+   code something like

+

+     if (riid == IID_ISomeInterface) {

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

+     } else {

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

+     }

+

+4. Implement the member functions of ISomeInterface. Use GetOwner() to

+   access the COM object class.

+

+And in your COM object class:

+

+5. Make the nested class a friend of the COM object class, and declare

+   an instance of the nested class as a member of the COM object class.

+

+   NOTE that because you must always pass the outer unknown and an hResult

+   to the CUnknown constructor you cannot use a default constructor, in

+   other words you will have to make the member variable a pointer to the

+   class and make a NEW call in your constructor to actually create it.

+

+6. override the NonDelegatingQueryInterface with code like this:

+

+     if (riid == IID_ISomeInterface) {

+         return m_pImplFilter->

+            NonDelegatingQueryInterface(IID_ISomeInterface, ppv);

+     } else {

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

+     }

+

+You can have mixed classes which support some interfaces via multiple

+inheritance and some via nested classes

+

+*/

+

+#ifndef __COMBASE__

+#define __COMBASE__

+

+// Filter Setup data structures no defined in axextend.idl

+

+typedef REGPINTYPES

+AMOVIESETUP_MEDIATYPE, * PAMOVIESETUP_MEDIATYPE, * FAR LPAMOVIESETUP_MEDIATYPE;

+

+typedef REGFILTERPINS

+AMOVIESETUP_PIN, * PAMOVIESETUP_PIN, * FAR LPAMOVIESETUP_PIN;

+

+typedef struct _AMOVIESETUP_FILTER

+{

+  const CLSID * clsID;

+  const WCHAR * strName;

+  DWORD      dwMerit;

+  UINT       nPins;

+  const AMOVIESETUP_PIN * lpPin;

+}

+AMOVIESETUP_FILTER, * PAMOVIESETUP_FILTER, * FAR LPAMOVIESETUP_FILTER;

+

+/* The DLLENTRY module initialises the module handle on loading */

+

+extern HINSTANCE g_hInst;

+

+/* On DLL load remember which platform we are running on */

+

+extern DWORD g_amPlatform;

+extern OSVERSIONINFO g_osInfo;     // Filled in by GetVersionEx

+

+/* Version of IUnknown that is renamed to allow a class to support both

+   non delegating and delegating IUnknowns in the same COM object */

+

+#ifndef INONDELEGATINGUNKNOWN_DEFINED

+DECLARE_INTERFACE(INonDelegatingUnknown)

+{

+    STDMETHOD(NonDelegatingQueryInterface) (THIS_ REFIID, LPVOID *) PURE;

+    STDMETHOD_(ULONG, NonDelegatingAddRef)(THIS) PURE;

+    STDMETHOD_(ULONG, NonDelegatingRelease)(THIS) PURE;

+};

+#define INONDELEGATINGUNKNOWN_DEFINED

+#endif

+

+typedef INonDelegatingUnknown *PNDUNKNOWN;

+

+

+/* This is the base object class that supports active object counting. As

+   part of the debug facilities we trace every time a C++ object is created

+   or destroyed. The name of the object has to be passed up through the class

+   derivation list during construction as you cannot call virtual functions

+   in the constructor. The downside of all this is that every single object

+   constructor has to take an object name parameter that describes it */

+

+class CBaseObject

+{

+

+private:

+

+    // Disable the copy constructor and assignment by default so you will get

+    //   compiler errors instead of unexpected behaviour if you pass objects

+    //   by value or assign objects.

+    CBaseObject(const CBaseObject& objectSrc);          // no implementation

+    void operator=(const CBaseObject& objectSrc);       // no implementation

+

+private:

+    static LONG m_cObjects;     /* Total number of objects active */

+

+protected:

+#ifdef DEBUG

+    DWORD m_dwCookie;           /* Cookie identifying this object */

+#endif

+

+

+public:

+

+    /* These increment and decrement the number of active objects */

+

+    CBaseObject(__in_opt LPCTSTR pName);

+#ifdef UNICODE

+    CBaseObject(__in_opt LPCSTR pName);

+#endif

+    ~CBaseObject();

+

+    /* Call this to find if there are any CUnknown derived objects active */

+

+    static LONG ObjectsActive() {

+        return m_cObjects;

+    };

+};

+

+

+/* An object that supports one or more COM interfaces will be based on

+   this class. It supports counting of total objects for DLLCanUnloadNow

+   support, and an implementation of the core non delegating IUnknown */

+

+class AM_NOVTABLE CUnknown : public INonDelegatingUnknown,

+                 public CBaseObject

+{

+private:

+    const LPUNKNOWN m_pUnknown; /* Owner of this object */

+

+protected:                      /* So we can override NonDelegatingRelease() */

+    volatile LONG m_cRef;       /* Number of reference counts */

+

+public:

+

+    CUnknown(__in_opt LPCTSTR pName, __in_opt LPUNKNOWN pUnk);

+    virtual ~CUnknown() {};

+

+    // This is redundant, just use the other constructor

+    //   as we never touch the HRESULT in this anyway

+    CUnknown(__in_opt LPCTSTR Name, __in_opt LPUNKNOWN pUnk, __inout_opt HRESULT *phr);

+#ifdef UNICODE

+    CUnknown(__in_opt LPCSTR pName, __in_opt LPUNKNOWN pUnk);

+    CUnknown(__in_opt LPCSTR pName, __in_opt LPUNKNOWN pUnk,__inout_opt HRESULT *phr);

+#endif

+

+    /* Return the owner of this object */

+

+    LPUNKNOWN GetOwner() const {

+        return m_pUnknown;

+    };

+

+    /* Called from the class factory to create a new instance, it is

+       pure virtual so it must be overriden in your derived class */

+

+    /* static CUnknown *CreateInstance(LPUNKNOWN, HRESULT *) */

+

+    /* Non delegating unknown implementation */

+

+    STDMETHODIMP NonDelegatingQueryInterface(REFIID, __deref_out void **);

+    STDMETHODIMP_(ULONG) NonDelegatingAddRef();

+    STDMETHODIMP_(ULONG) NonDelegatingRelease();

+};

+

+/* Return an interface pointer to a requesting client

+   performing a thread safe AddRef as necessary */

+

+STDAPI GetInterface(LPUNKNOWN pUnk, __out void **ppv);

+

+/* A function that can create a new COM object */

+

+typedef CUnknown *(CALLBACK *LPFNNewCOMObject)(__in_opt LPUNKNOWN pUnkOuter, __inout_opt HRESULT *phr);

+

+/*  A function (can be NULL) which is called from the DLL entrypoint

+    routine for each factory template:

+

+    bLoading - TRUE on DLL load, FALSE on DLL unload

+    rclsid   - the m_ClsID of the entry

+*/

+typedef void (CALLBACK *LPFNInitRoutine)(BOOL bLoading, const CLSID *rclsid);

+

+/* Create one of these per object class in an array so that

+   the default class factory code can create new instances */

+

+class CFactoryTemplate {

+

+public:

+

+    const WCHAR *              m_Name;

+    const CLSID *              m_ClsID;

+    LPFNNewCOMObject           m_lpfnNew;

+    LPFNInitRoutine            m_lpfnInit;

+    const AMOVIESETUP_FILTER * m_pAMovieSetup_Filter;

+

+    BOOL IsClassID(REFCLSID rclsid) const {

+        return (IsEqualCLSID(*m_ClsID,rclsid));

+    };

+

+    CUnknown *CreateInstance(__inout_opt LPUNKNOWN pUnk, __inout_opt HRESULT *phr) const {

+        CheckPointer(phr,NULL);

+        return m_lpfnNew(pUnk, phr);

+    };

+};

+

+

+/* You must override the (pure virtual) NonDelegatingQueryInterface to return

+   interface pointers (using GetInterface) to the interfaces your derived

+   class supports (the default implementation only supports IUnknown) */

+

+#define DECLARE_IUNKNOWN                                        \

+    STDMETHODIMP QueryInterface(REFIID riid, __deref_out void **ppv) {      \

+        return GetOwner()->QueryInterface(riid,ppv);            \

+    };                                                          \

+    STDMETHODIMP_(ULONG) AddRef() {                             \

+        return GetOwner()->AddRef();                            \

+    };                                                          \

+    STDMETHODIMP_(ULONG) Release() {                            \

+        return GetOwner()->Release();                           \

+    };

+

+

+

+HINSTANCE	LoadOLEAut32();

+

+

+#endif /* __COMBASE__ */

+

+

+

+

diff --git a/jni/pjproject-android/third_party/BaseClasses/ctlutil.cpp b/jni/pjproject-android/third_party/BaseClasses/ctlutil.cpp
new file mode 100644
index 0000000..2902df2
--- /dev/null
+++ b/jni/pjproject-android/third_party/BaseClasses/ctlutil.cpp
@@ -0,0 +1,2545 @@
+//------------------------------------------------------------------------------

+// File: CtlUtil.cpp

+//

+// Desc: DirectShow base classes.

+//

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

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

+

+

+// Base classes implementing IDispatch parsing for the basic control dual

+// interfaces. Derive from these and implement just the custom method and

+// property methods. We also implement CPosPassThru that can be used by

+// renderers and transforms to pass by IMediaPosition and IMediaSeeking

+

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

+

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

+

+#include <streams.h>

+#include <limits.h>

+#include "seekpt.h"

+

+// 'bool' non standard reserved word

+#pragma warning(disable:4237)

+

+

+// --- CBaseDispatch implementation ----------

+CBaseDispatch::~CBaseDispatch()

+{

+    if (m_pti) {

+	m_pti->Release();

+    }

+}

+

+

+// return 1 if we support GetTypeInfo

+

+STDMETHODIMP

+CBaseDispatch::GetTypeInfoCount(__out UINT * pctinfo)

+{

+    CheckPointer(pctinfo,E_POINTER);

+    ValidateReadWritePtr(pctinfo,sizeof(UINT *));

+    *pctinfo = 1;

+    return S_OK;

+}

+

+

+typedef HRESULT (STDAPICALLTYPE *LPLOADTYPELIB)(

+			    const OLECHAR FAR *szFile,

+			    __deref_out ITypeLib FAR* FAR* pptlib);

+

+typedef HRESULT (STDAPICALLTYPE *LPLOADREGTYPELIB)(REFGUID rguid,

+			    WORD wVerMajor,

+			    WORD wVerMinor,

+			    LCID lcid,

+			    __deref_out ITypeLib FAR* FAR* pptlib);

+

+// attempt to find our type library

+

+STDMETHODIMP

+CBaseDispatch::GetTypeInfo(

+  REFIID riid,

+  UINT itinfo,

+  LCID lcid,

+  __deref_out ITypeInfo ** pptinfo)

+{

+    CheckPointer(pptinfo,E_POINTER);

+    ValidateReadWritePtr(pptinfo,sizeof(ITypeInfo *));

+    HRESULT hr;

+

+    *pptinfo = NULL;

+

+    // we only support one type element

+    if (0 != itinfo) {

+	return TYPE_E_ELEMENTNOTFOUND;

+    }

+

+    if (NULL == pptinfo) {

+	return E_POINTER;

+    }

+

+    // always look for neutral

+    if (NULL == m_pti) {

+

+	LPLOADTYPELIB	    lpfnLoadTypeLib;

+	LPLOADREGTYPELIB    lpfnLoadRegTypeLib;

+	ITypeLib	    *ptlib;

+	HINSTANCE	    hInst;

+

+	static const char  szTypeLib[]	  = "LoadTypeLib";

+	static const char  szRegTypeLib[] = "LoadRegTypeLib";

+	static const WCHAR szControl[]	  = L"control.tlb";

+

+	//

+	// Try to get the Ole32Aut.dll module handle.

+	//

+

+	hInst = LoadOLEAut32();

+	if (hInst == NULL) {

+	    DWORD dwError = GetLastError();

+	    return AmHresultFromWin32(dwError);

+	}

+	lpfnLoadRegTypeLib = (LPLOADREGTYPELIB)GetProcAddress(hInst,

+							      szRegTypeLib);

+	if (lpfnLoadRegTypeLib == NULL) {

+	    DWORD dwError = GetLastError();

+	    return AmHresultFromWin32(dwError);

+	}

+

+	hr = (*lpfnLoadRegTypeLib)(LIBID_QuartzTypeLib, 1, 0, // version 1.0

+				   lcid, &ptlib);

+

+	if (FAILED(hr)) {

+

+	    // attempt to load directly - this will fill the

+	    // registry in if it finds it

+

+	    lpfnLoadTypeLib = (LPLOADTYPELIB)GetProcAddress(hInst, szTypeLib);

+	    if (lpfnLoadTypeLib == NULL) {

+		DWORD dwError = GetLastError();

+		return AmHresultFromWin32(dwError);

+	    }

+

+	    hr = (*lpfnLoadTypeLib)(szControl, &ptlib);

+	    if (FAILED(hr)) {

+		return hr;

+	    }

+	}

+

+	hr = ptlib->GetTypeInfoOfGuid(

+		    riid,

+		    &m_pti);

+

+	ptlib->Release();

+

+	if (FAILED(hr)) {

+	    return hr;

+	}

+    }

+

+    *pptinfo = m_pti;

+    m_pti->AddRef();

+    return S_OK;

+}

+

+

+STDMETHODIMP

+CBaseDispatch::GetIDsOfNames(

+  REFIID riid,

+  __in_ecount(cNames) LPOLESTR * rgszNames,

+  UINT cNames,

+  LCID lcid,

+  __out_ecount(cNames) DISPID * rgdispid)

+{

+    // although the IDispatch riid is dead, we use this to pass from

+    // the interface implementation class to us the iid we are talking about.

+

+    ITypeInfo * pti;

+    HRESULT hr = GetTypeInfo(riid, 0, lcid, &pti);

+

+    if (SUCCEEDED(hr)) {

+	hr = pti->GetIDsOfNames(rgszNames, cNames, rgdispid);

+

+	pti->Release();

+    }

+    return hr;

+}

+

+

+// --- CMediaControl implementation ---------

+

+CMediaControl::CMediaControl(const TCHAR * name,LPUNKNOWN pUnk) :

+    CUnknown(name, pUnk)

+{

+}

+

+// expose our interfaces IMediaControl and IUnknown

+

+STDMETHODIMP

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

+{

+    ValidateReadWritePtr(ppv,sizeof(PVOID));

+    if (riid == IID_IMediaControl) {

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

+    } else {

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

+    }

+}

+

+

+// return 1 if we support GetTypeInfo

+

+STDMETHODIMP

+CMediaControl::GetTypeInfoCount(__out UINT * pctinfo)

+{

+    return m_basedisp.GetTypeInfoCount(pctinfo);

+}

+

+

+// attempt to find our type library

+

+STDMETHODIMP

+CMediaControl::GetTypeInfo(

+  UINT itinfo,

+  LCID lcid,

+  __deref_out ITypeInfo ** pptinfo)

+{

+    return m_basedisp.GetTypeInfo(

+		IID_IMediaControl,

+		itinfo,

+		lcid,

+		pptinfo);

+}

+

+

+STDMETHODIMP

+CMediaControl::GetIDsOfNames(

+  REFIID riid,

+  __in_ecount(cNames) LPOLESTR * rgszNames,

+  UINT cNames,

+  LCID lcid,

+  __out_ecount(cNames) DISPID * rgdispid)

+{

+    return m_basedisp.GetIDsOfNames(

+			IID_IMediaControl,

+			rgszNames,

+			cNames,

+			lcid,

+			rgdispid);

+}

+

+

+STDMETHODIMP

+CMediaControl::Invoke(

+  DISPID dispidMember,

+  REFIID riid,

+  LCID lcid,

+  WORD wFlags,

+  __in DISPPARAMS * pdispparams,

+  __out_opt VARIANT * pvarResult,

+  __out_opt EXCEPINFO * pexcepinfo,

+  __out_opt UINT * puArgErr)

+{

+    // this parameter is a dead leftover from an earlier interface

+    if (IID_NULL != riid) {

+	return DISP_E_UNKNOWNINTERFACE;

+    }

+

+    ITypeInfo * pti;

+    HRESULT hr = GetTypeInfo(0, lcid, &pti);

+

+    if (FAILED(hr)) {

+	return hr;

+    }

+

+    hr = pti->Invoke(

+	    (IMediaControl *)this,

+	    dispidMember,

+	    wFlags,

+	    pdispparams,

+	    pvarResult,

+	    pexcepinfo,

+	    puArgErr);

+

+    pti->Release();

+    return hr;

+}

+

+

+// --- CMediaEvent implementation ----------

+

+

+CMediaEvent::CMediaEvent(__in_opt LPCTSTR name,__in_opt LPUNKNOWN pUnk) :

+    CUnknown(name, pUnk)

+{

+}

+

+

+// expose our interfaces IMediaEvent and IUnknown

+

+STDMETHODIMP

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

+{

+    ValidateReadWritePtr(ppv,sizeof(PVOID));

+    if (riid == IID_IMediaEvent || riid == IID_IMediaEventEx) {

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

+    } else {

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

+    }

+}

+

+

+// return 1 if we support GetTypeInfo

+

+STDMETHODIMP

+CMediaEvent::GetTypeInfoCount(__out UINT * pctinfo)

+{

+    return m_basedisp.GetTypeInfoCount(pctinfo);

+}

+

+

+// attempt to find our type library

+

+STDMETHODIMP

+CMediaEvent::GetTypeInfo(

+  UINT itinfo,

+  LCID lcid,

+  __deref_out ITypeInfo ** pptinfo)

+{

+    return m_basedisp.GetTypeInfo(

+		IID_IMediaEvent,

+		itinfo,

+		lcid,

+		pptinfo);

+}

+

+

+STDMETHODIMP

+CMediaEvent::GetIDsOfNames(

+  REFIID riid,

+  __in_ecount(cNames) LPOLESTR * rgszNames,

+  UINT cNames,

+  LCID lcid,

+  __out_ecount(cNames) DISPID * rgdispid)

+{

+    return m_basedisp.GetIDsOfNames(

+			IID_IMediaEvent,

+			rgszNames,

+			cNames,

+			lcid,

+			rgdispid);

+}

+

+

+STDMETHODIMP

+CMediaEvent::Invoke(

+  DISPID dispidMember,

+  REFIID riid,

+  LCID lcid,

+  WORD wFlags,

+  __in DISPPARAMS * pdispparams,

+  __out_opt VARIANT * pvarResult,

+  __out_opt EXCEPINFO * pexcepinfo,

+  __out_opt UINT * puArgErr)

+{

+    // this parameter is a dead leftover from an earlier interface

+    if (IID_NULL != riid) {

+	return DISP_E_UNKNOWNINTERFACE;

+    }

+

+    ITypeInfo * pti;

+    HRESULT hr = GetTypeInfo(0, lcid, &pti);

+

+    if (FAILED(hr)) {

+	return hr;

+    }

+

+    hr = pti->Invoke(

+	    (IMediaEvent *)this,

+	    dispidMember,

+	    wFlags,

+	    pdispparams,

+	    pvarResult,

+	    pexcepinfo,

+	    puArgErr);

+

+    pti->Release();

+    return hr;

+}

+

+

+// --- CMediaPosition implementation ----------

+

+

+CMediaPosition::CMediaPosition(__in_opt LPCTSTR name,__in_opt LPUNKNOWN pUnk) :

+    CUnknown(name, pUnk)

+{

+}

+

+CMediaPosition::CMediaPosition(__in_opt LPCTSTR name,

+                               __in_opt LPUNKNOWN pUnk,

+                               __inout HRESULT * phr) :

+    CUnknown(name, pUnk)

+{

+    UNREFERENCED_PARAMETER(phr);

+}

+

+

+// expose our interfaces IMediaPosition and IUnknown

+

+STDMETHODIMP

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

+{

+    ValidateReadWritePtr(ppv,sizeof(PVOID));

+    if (riid == IID_IMediaPosition) {

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

+    } else {

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

+    }

+}

+

+

+// return 1 if we support GetTypeInfo

+

+STDMETHODIMP

+CMediaPosition::GetTypeInfoCount(__out UINT * pctinfo)

+{

+    return m_basedisp.GetTypeInfoCount(pctinfo);

+}

+

+

+// attempt to find our type library

+

+STDMETHODIMP

+CMediaPosition::GetTypeInfo(

+  UINT itinfo,

+  LCID lcid,

+  __deref_out ITypeInfo ** pptinfo)

+{

+    return m_basedisp.GetTypeInfo(

+		IID_IMediaPosition,

+		itinfo,

+		lcid,

+		pptinfo);

+}

+

+

+STDMETHODIMP

+CMediaPosition::GetIDsOfNames(

+  REFIID riid,

+  __in_ecount(cNames) LPOLESTR * rgszNames,

+  UINT cNames,

+  LCID lcid,

+  __out_ecount(cNames) DISPID * rgdispid)

+{

+    return m_basedisp.GetIDsOfNames(

+			IID_IMediaPosition,

+			rgszNames,

+			cNames,

+			lcid,

+			rgdispid);

+}

+

+

+STDMETHODIMP

+CMediaPosition::Invoke(

+  DISPID dispidMember,

+  REFIID riid,

+  LCID lcid,

+  WORD wFlags,

+  __in DISPPARAMS * pdispparams,

+  __out_opt VARIANT * pvarResult,

+  __out_opt EXCEPINFO * pexcepinfo,

+  __out_opt UINT * puArgErr)

+{

+    // this parameter is a dead leftover from an earlier interface

+    if (IID_NULL != riid) {

+	return DISP_E_UNKNOWNINTERFACE;

+    }

+

+    ITypeInfo * pti;

+    HRESULT hr = GetTypeInfo(0, lcid, &pti);

+

+    if (FAILED(hr)) {

+	return hr;

+    }

+

+    hr = pti->Invoke(

+	    (IMediaPosition *)this,

+	    dispidMember,

+	    wFlags,

+	    pdispparams,

+	    pvarResult,

+	    pexcepinfo,

+	    puArgErr);

+

+    pti->Release();

+    return hr;

+}

+

+

+// --- IMediaPosition and IMediaSeeking pass through class ----------

+

+

+CPosPassThru::CPosPassThru(__in_opt LPCTSTR pName,

+			   __in_opt LPUNKNOWN pUnk,

+			   __inout HRESULT *phr,

+			   IPin *pPin) :

+    CMediaPosition(pName,pUnk),

+    m_pPin(pPin)

+{

+    if (pPin == NULL) {

+	*phr = E_POINTER;

+	return;

+    }

+}

+

+

+// Expose our IMediaSeeking and IMediaPosition interfaces

+

+STDMETHODIMP

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

+{

+    CheckPointer(ppv,E_POINTER);

+    *ppv = NULL;

+

+    if (riid == IID_IMediaSeeking) {

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

+    }

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

+}

+

+

+// Return the IMediaPosition interface from our peer

+

+HRESULT

+CPosPassThru::GetPeer(IMediaPosition ** ppMP)

+{

+    *ppMP = NULL;

+

+    IPin *pConnected;

+    HRESULT hr = m_pPin->ConnectedTo(&pConnected);

+    if (FAILED(hr)) {

+	return E_NOTIMPL;

+    }

+    IMediaPosition * pMP;

+    hr = pConnected->QueryInterface(IID_IMediaPosition, (void **) &pMP);

+    pConnected->Release();

+    if (FAILED(hr)) {

+	return E_NOTIMPL;

+    }

+

+    *ppMP = pMP;

+    return S_OK;

+}

+

+

+// Return the IMediaSeeking interface from our peer

+

+HRESULT

+CPosPassThru::GetPeerSeeking(__deref_out IMediaSeeking ** ppMS)

+{

+    *ppMS = NULL;

+

+    IPin *pConnected;

+    HRESULT hr = m_pPin->ConnectedTo(&pConnected);

+    if (FAILED(hr)) {

+	return E_NOTIMPL;

+    }

+    IMediaSeeking * pMS;

+    hr = pConnected->QueryInterface(IID_IMediaSeeking, (void **) &pMS);

+    pConnected->Release();

+    if (FAILED(hr)) {

+	return E_NOTIMPL;

+    }

+

+    *ppMS = pMS;

+    return S_OK;

+}

+

+

+// --- IMediaSeeking methods ----------

+

+

+STDMETHODIMP

+CPosPassThru::GetCapabilities(__out DWORD * pCaps)

+{

+    IMediaSeeking* pMS;

+    HRESULT hr = GetPeerSeeking(&pMS);

+    if (FAILED(hr)) {

+	return hr;

+    }

+

+    hr = pMS->GetCapabilities(pCaps);

+    pMS->Release();

+    return hr;

+}

+

+STDMETHODIMP

+CPosPassThru::CheckCapabilities(__inout DWORD * pCaps)

+{

+    IMediaSeeking* pMS;

+    HRESULT hr = GetPeerSeeking(&pMS);

+    if (FAILED(hr)) {

+	return hr;

+    }

+

+    hr = pMS->CheckCapabilities(pCaps);

+    pMS->Release();

+    return hr;

+}

+

+STDMETHODIMP

+CPosPassThru::IsFormatSupported(const GUID * pFormat)

+{

+    IMediaSeeking* pMS;

+    HRESULT hr = GetPeerSeeking(&pMS);

+    if (FAILED(hr)) {

+	return hr;

+    }

+

+    hr = pMS->IsFormatSupported(pFormat);

+    pMS->Release();

+    return hr;

+}

+

+

+STDMETHODIMP

+CPosPassThru::QueryPreferredFormat(__out GUID *pFormat)

+{

+    IMediaSeeking* pMS;

+    HRESULT hr = GetPeerSeeking(&pMS);

+    if (FAILED(hr)) {

+	return hr;

+    }

+

+    hr = pMS->QueryPreferredFormat(pFormat);

+    pMS->Release();

+    return hr;

+}

+

+

+STDMETHODIMP

+CPosPassThru::SetTimeFormat(const GUID * pFormat)

+{

+    IMediaSeeking* pMS;

+    HRESULT hr = GetPeerSeeking(&pMS);

+    if (FAILED(hr)) {

+	return hr;

+    }

+

+    hr = pMS->SetTimeFormat(pFormat);

+    pMS->Release();

+    return hr;

+}

+

+

+STDMETHODIMP

+CPosPassThru::GetTimeFormat(__out GUID *pFormat)

+{

+    IMediaSeeking* pMS;

+    HRESULT hr = GetPeerSeeking(&pMS);

+    if (FAILED(hr)) {

+	return hr;

+    }

+

+    hr = pMS->GetTimeFormat(pFormat);

+    pMS->Release();

+    return hr;

+}

+

+

+STDMETHODIMP

+CPosPassThru::IsUsingTimeFormat(const GUID * pFormat)

+{

+    IMediaSeeking* pMS;

+    HRESULT hr = GetPeerSeeking(&pMS);

+    if (FAILED(hr)) {

+	return hr;

+    }

+

+    hr = pMS->IsUsingTimeFormat(pFormat);

+    pMS->Release();

+    return hr;

+}

+

+

+STDMETHODIMP

+CPosPassThru::ConvertTimeFormat(__out LONGLONG * pTarget, 

+                                __in_opt const GUID * pTargetFormat,

+				LONGLONG Source, 

+                                __in_opt const GUID * pSourceFormat )

+{

+    IMediaSeeking* pMS;

+    HRESULT hr = GetPeerSeeking(&pMS);

+    if (FAILED(hr)) {

+	return hr;

+    }

+

+    hr = pMS->ConvertTimeFormat(pTarget, pTargetFormat, Source, pSourceFormat );

+    pMS->Release();

+    return hr;

+}

+

+

+STDMETHODIMP

+CPosPassThru::SetPositions( __inout_opt LONGLONG * pCurrent, 

+                            DWORD CurrentFlags, 

+                            __inout_opt LONGLONG * pStop, 

+                            DWORD StopFlags )

+{

+    IMediaSeeking* pMS;

+    HRESULT hr = GetPeerSeeking(&pMS);

+    if (FAILED(hr)) {

+	return hr;

+    }

+

+    hr = pMS->SetPositions(pCurrent, CurrentFlags, pStop, StopFlags );

+    pMS->Release();

+    return hr;

+}

+

+STDMETHODIMP

+CPosPassThru::GetPositions(__out_opt LONGLONG *pCurrent, __out_opt LONGLONG * pStop)

+{

+    IMediaSeeking* pMS;

+    HRESULT hr = GetPeerSeeking(&pMS);

+    if (FAILED(hr)) {

+	return hr;

+    }

+

+    hr = pMS->GetPositions(pCurrent,pStop);

+    pMS->Release();

+    return hr;

+}

+

+HRESULT

+CPosPassThru::GetSeekingLongLong

+( HRESULT (__stdcall IMediaSeeking::*pMethod)( __out LONGLONG * )

+, LONGLONG * pll

+)

+{

+    IMediaSeeking* pMS;

+    HRESULT hr = GetPeerSeeking(&pMS);

+    if (SUCCEEDED(hr))

+    {

+	hr = (pMS->*pMethod)(pll);

+	pMS->Release();

+    }

+    return hr;

+}

+

+// If we don't have a current position then ask upstream

+

+STDMETHODIMP

+CPosPassThru::GetCurrentPosition(__out LONGLONG *pCurrent)

+{

+    // Can we report the current position

+    HRESULT hr = GetMediaTime(pCurrent,NULL);

+    if (SUCCEEDED(hr)) hr = NOERROR;

+    else hr = GetSeekingLongLong( &IMediaSeeking::GetCurrentPosition, pCurrent );

+    return hr;

+}

+

+

+STDMETHODIMP

+CPosPassThru::GetStopPosition(__out LONGLONG *pStop)

+{

+    return GetSeekingLongLong( &IMediaSeeking::GetStopPosition, pStop );;

+}

+

+STDMETHODIMP

+CPosPassThru::GetDuration(__out LONGLONG *pDuration)

+{

+    return GetSeekingLongLong( &IMediaSeeking::GetDuration, pDuration );;

+}

+

+

+STDMETHODIMP

+CPosPassThru::GetPreroll(__out LONGLONG *pllPreroll)

+{

+    return GetSeekingLongLong( &IMediaSeeking::GetPreroll, pllPreroll );;

+}

+

+

+STDMETHODIMP

+CPosPassThru::GetAvailable( __out_opt LONGLONG *pEarliest, __out_opt LONGLONG *pLatest )

+{

+    IMediaSeeking* pMS;

+    HRESULT hr = GetPeerSeeking(&pMS);

+    if (FAILED(hr)) {

+	return hr;

+    }

+

+    hr = pMS->GetAvailable( pEarliest, pLatest );

+    pMS->Release();

+    return hr;

+}

+

+

+STDMETHODIMP

+CPosPassThru::GetRate(__out double * pdRate)

+{

+    IMediaSeeking* pMS;

+    HRESULT hr = GetPeerSeeking(&pMS);

+    if (FAILED(hr)) {

+	return hr;

+    }

+    hr = pMS->GetRate(pdRate);

+    pMS->Release();

+    return hr;

+}

+

+

+STDMETHODIMP

+CPosPassThru::SetRate(double dRate)

+{

+    if (0.0 == dRate) {

+		return E_INVALIDARG;

+    }

+

+    IMediaSeeking* pMS;

+    HRESULT hr = GetPeerSeeking(&pMS);

+    if (FAILED(hr)) {

+	return hr;

+    }

+    hr = pMS->SetRate(dRate);

+    pMS->Release();

+    return hr;

+}

+

+

+

+

+// --- IMediaPosition methods ----------

+

+

+STDMETHODIMP

+CPosPassThru::get_Duration(__out REFTIME * plength)

+{

+    IMediaPosition* pMP;

+    HRESULT hr = GetPeer(&pMP);

+    if (FAILED(hr)) {

+	return hr;

+    }

+

+    hr = pMP->get_Duration(plength);

+    pMP->Release();

+    return hr;

+}

+

+

+STDMETHODIMP

+CPosPassThru::get_CurrentPosition(__out REFTIME * pllTime)

+{

+    IMediaPosition* pMP;

+    HRESULT hr = GetPeer(&pMP);

+    if (FAILED(hr)) {

+	return hr;

+    }

+    hr = pMP->get_CurrentPosition(pllTime);

+    pMP->Release();

+    return hr;

+}

+

+

+STDMETHODIMP

+CPosPassThru::put_CurrentPosition(REFTIME llTime)

+{

+    IMediaPosition* pMP;

+    HRESULT hr = GetPeer(&pMP);

+    if (FAILED(hr)) {

+	return hr;

+    }

+    hr = pMP->put_CurrentPosition(llTime);

+    pMP->Release();

+    return hr;

+}

+

+

+STDMETHODIMP

+CPosPassThru::get_StopTime(__out REFTIME * pllTime)

+{

+    IMediaPosition* pMP;

+    HRESULT hr = GetPeer(&pMP);

+    if (FAILED(hr)) {

+	return hr;

+    }

+    hr = pMP->get_StopTime(pllTime);

+    pMP->Release();

+    return hr;

+}

+

+

+STDMETHODIMP

+CPosPassThru::put_StopTime(REFTIME llTime)

+{

+    IMediaPosition* pMP;

+    HRESULT hr = GetPeer(&pMP);

+    if (FAILED(hr)) {

+	return hr;

+    }

+    hr = pMP->put_StopTime(llTime);

+    pMP->Release();

+    return hr;

+}

+

+

+STDMETHODIMP

+CPosPassThru::get_PrerollTime(__out REFTIME * pllTime)

+{

+    IMediaPosition* pMP;

+    HRESULT hr = GetPeer(&pMP);

+    if (FAILED(hr)) {

+	return hr;

+    }

+    hr = pMP->get_PrerollTime(pllTime);

+    pMP->Release();

+    return hr;

+}

+

+

+STDMETHODIMP

+CPosPassThru::put_PrerollTime(REFTIME llTime)

+{

+    IMediaPosition* pMP;

+    HRESULT hr = GetPeer(&pMP);

+    if (FAILED(hr)) {

+	return hr;

+    }

+    hr = pMP->put_PrerollTime(llTime);

+    pMP->Release();

+    return hr;

+}

+

+

+STDMETHODIMP

+CPosPassThru::get_Rate(__out double * pdRate)

+{

+    IMediaPosition* pMP;

+    HRESULT hr = GetPeer(&pMP);

+    if (FAILED(hr)) {

+	return hr;

+    }

+    hr = pMP->get_Rate(pdRate);

+    pMP->Release();

+    return hr;

+}

+

+

+STDMETHODIMP

+CPosPassThru::put_Rate(double dRate)

+{

+    if (0.0 == dRate) {

+		return E_INVALIDARG;

+    }

+

+    IMediaPosition* pMP;

+    HRESULT hr = GetPeer(&pMP);

+    if (FAILED(hr)) {

+	return hr;

+    }

+    hr = pMP->put_Rate(dRate);

+    pMP->Release();

+    return hr;

+}

+

+

+STDMETHODIMP

+CPosPassThru::CanSeekForward(__out LONG *pCanSeekForward)

+{

+    IMediaPosition* pMP;

+    HRESULT hr = GetPeer(&pMP);

+    if (FAILED(hr)) {

+	return hr;

+    }

+    hr = pMP->CanSeekForward(pCanSeekForward);

+    pMP->Release();

+    return hr;

+}

+

+

+STDMETHODIMP

+CPosPassThru::CanSeekBackward(__out LONG *pCanSeekBackward)

+{

+    IMediaPosition* pMP;

+    HRESULT hr = GetPeer(&pMP);

+    if (FAILED(hr)) {

+	return hr;

+    }

+    hr = pMP->CanSeekBackward(pCanSeekBackward);

+    pMP->Release();

+    return hr;

+}

+

+

+// --- Implements the CRendererPosPassThru class ----------

+

+

+// Media times (eg current frame, field, sample etc) are passed through the

+// filtergraph in media samples. When a renderer gets a sample with media

+// times in it, it will call one of the RegisterMediaTime methods we expose

+// (one takes an IMediaSample, the other takes the media times direct). We

+// store the media times internally and return them in GetCurrentPosition.

+

+CRendererPosPassThru::CRendererPosPassThru(__in_opt LPCTSTR pName,

+					   __in_opt LPUNKNOWN pUnk,

+					   __inout HRESULT *phr,

+					   IPin *pPin) :

+    CPosPassThru(pName,pUnk,phr,pPin),

+    m_StartMedia(0),

+    m_EndMedia(0),

+    m_bReset(TRUE)

+{

+}

+

+

+// Sets the media times the object should report

+

+HRESULT

+CRendererPosPassThru::RegisterMediaTime(IMediaSample *pMediaSample)

+{

+    ASSERT(pMediaSample);

+    LONGLONG StartMedia;

+    LONGLONG EndMedia;

+

+    CAutoLock cAutoLock(&m_PositionLock);

+

+    // Get the media times from the sample

+

+    HRESULT hr = pMediaSample->GetTime(&StartMedia,&EndMedia);

+    if (FAILED(hr))

+    {

+	ASSERT(hr == VFW_E_SAMPLE_TIME_NOT_SET);

+	return hr;

+    }

+

+    m_StartMedia = StartMedia;

+    m_EndMedia = EndMedia;

+    m_bReset = FALSE;

+    return NOERROR;

+}

+

+

+// Sets the media times the object should report

+

+HRESULT

+CRendererPosPassThru::RegisterMediaTime(LONGLONG StartTime,LONGLONG EndTime)

+{

+    CAutoLock cAutoLock(&m_PositionLock);

+    m_StartMedia = StartTime;

+    m_EndMedia = EndTime;

+    m_bReset = FALSE;

+    return NOERROR;

+}

+

+

+// Return the current media times registered in the object

+

+HRESULT

+CRendererPosPassThru::GetMediaTime(__out LONGLONG *pStartTime, __out_opt LONGLONG *pEndTime)

+{

+    ASSERT(pStartTime);

+

+    CAutoLock cAutoLock(&m_PositionLock);

+    if (m_bReset == TRUE) {

+	return E_FAIL;

+    }

+

+    // We don't have to return the end time

+

+    HRESULT hr = ConvertTimeFormat( pStartTime, 0, m_StartMedia, &TIME_FORMAT_MEDIA_TIME );

+    if (pEndTime && SUCCEEDED(hr)) {

+	hr = ConvertTimeFormat( pEndTime, 0, m_EndMedia, &TIME_FORMAT_MEDIA_TIME );

+    }

+    return hr;

+}

+

+

+// Resets the media times we hold

+

+HRESULT

+CRendererPosPassThru::ResetMediaTime()

+{

+    CAutoLock cAutoLock(&m_PositionLock);

+    m_StartMedia = 0;

+    m_EndMedia = 0;

+    m_bReset = TRUE;

+    return NOERROR;

+}

+

+// Intended to be called by the owing filter during EOS processing so

+// that the media times can be adjusted to the stop time.  This ensures

+// that the GetCurrentPosition will actully get to the stop position.

+HRESULT

+CRendererPosPassThru::EOS()

+{

+    HRESULT hr;

+

+    if ( m_bReset == TRUE ) hr = E_FAIL;

+    else

+    {

+	LONGLONG llStop;

+	if SUCCEEDED(hr=GetStopPosition(&llStop))

+	{

+	    CAutoLock cAutoLock(&m_PositionLock);

+	    m_StartMedia =

+	    m_EndMedia	 = llStop;

+	}

+    }

+    return hr;

+}

+

+// -- CSourceSeeking implementation ------------

+

+CSourceSeeking::CSourceSeeking(

+    __in_opt LPCTSTR pName,

+    __in_opt LPUNKNOWN pUnk,

+    __inout HRESULT* phr,

+    __in CCritSec * pLock) :

+        CUnknown(pName, pUnk),

+        m_pLock(pLock),

+        m_rtStart((long)0)

+{

+    m_rtStop = _I64_MAX / 2;

+    m_rtDuration = m_rtStop;

+    m_dRateSeeking = 1.0;

+

+    m_dwSeekingCaps = AM_SEEKING_CanSeekForwards

+        | AM_SEEKING_CanSeekBackwards

+        | AM_SEEKING_CanSeekAbsolute

+        | AM_SEEKING_CanGetStopPos

+        | AM_SEEKING_CanGetDuration;

+}

+

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

+{

+    if(riid == IID_IMediaSeeking) {

+        CheckPointer(ppv, E_POINTER);

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

+    }

+    else {

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

+    }

+}

+

+

+HRESULT CSourceSeeking::IsFormatSupported(const GUID * pFormat)

+{

+    CheckPointer(pFormat, E_POINTER);

+    // only seeking in time (REFERENCE_TIME units) is supported

+    return *pFormat == TIME_FORMAT_MEDIA_TIME ? S_OK : S_FALSE;

+}

+

+HRESULT CSourceSeeking::QueryPreferredFormat(__out GUID *pFormat)

+{

+    CheckPointer(pFormat, E_POINTER);

+    *pFormat = TIME_FORMAT_MEDIA_TIME;

+    return S_OK;

+}

+

+HRESULT CSourceSeeking::SetTimeFormat(const GUID * pFormat)

+{

+    CheckPointer(pFormat, E_POINTER);

+

+    // nothing to set; just check that it's TIME_FORMAT_TIME

+    return *pFormat == TIME_FORMAT_MEDIA_TIME ? S_OK : E_INVALIDARG;

+}

+

+HRESULT CSourceSeeking::IsUsingTimeFormat(const GUID * pFormat)

+{

+    CheckPointer(pFormat, E_POINTER);

+    return *pFormat == TIME_FORMAT_MEDIA_TIME ? S_OK : S_FALSE;

+}

+

+HRESULT CSourceSeeking::GetTimeFormat(__out GUID *pFormat)

+{

+    CheckPointer(pFormat, E_POINTER);

+    *pFormat = TIME_FORMAT_MEDIA_TIME;

+    return S_OK;

+}

+

+HRESULT CSourceSeeking::GetDuration(__out LONGLONG *pDuration)

+{

+    CheckPointer(pDuration, E_POINTER);

+    CAutoLock lock(m_pLock);

+    *pDuration = m_rtDuration;

+    return S_OK;

+}

+

+HRESULT CSourceSeeking::GetStopPosition(__out LONGLONG *pStop)

+{

+    CheckPointer(pStop, E_POINTER);

+    CAutoLock lock(m_pLock);

+    *pStop = m_rtStop;

+    return S_OK;

+}

+

+HRESULT CSourceSeeking::GetCurrentPosition(__out LONGLONG *pCurrent)

+{

+    // GetCurrentPosition is typically supported only in renderers and

+    // not in source filters.

+    return E_NOTIMPL;

+}

+

+HRESULT CSourceSeeking::GetCapabilities( __out DWORD * pCapabilities )

+{

+    CheckPointer(pCapabilities, E_POINTER);

+    *pCapabilities = m_dwSeekingCaps;

+    return S_OK;

+}

+

+HRESULT CSourceSeeking::CheckCapabilities( __inout DWORD * pCapabilities )

+{

+    CheckPointer(pCapabilities, E_POINTER);

+

+    // make sure all requested capabilities are in our mask

+    return (~m_dwSeekingCaps & *pCapabilities) ? S_FALSE : S_OK;

+}

+

+HRESULT CSourceSeeking::ConvertTimeFormat( __out LONGLONG * pTarget, 

+                                           __in_opt const GUID * pTargetFormat,

+                                           LONGLONG Source, 

+                                           __in_opt const GUID * pSourceFormat )

+{

+    CheckPointer(pTarget, E_POINTER);

+    // format guids can be null to indicate current format

+

+    // since we only support TIME_FORMAT_MEDIA_TIME, we don't really

+    // offer any conversions.

+    if(pTargetFormat == 0 || *pTargetFormat == TIME_FORMAT_MEDIA_TIME)

+    {

+        if(pSourceFormat == 0 || *pSourceFormat == TIME_FORMAT_MEDIA_TIME)

+        {

+            *pTarget = Source;

+            return S_OK;

+        }

+    }

+

+    return E_INVALIDARG;

+}

+

+

+HRESULT CSourceSeeking::SetPositions( __inout_opt LONGLONG * pCurrent,  

+                                      DWORD CurrentFlags, 

+                                      __inout_opt LONGLONG * pStop,  

+                                      DWORD StopFlags )

+{

+    DWORD StopPosBits = StopFlags & AM_SEEKING_PositioningBitsMask;

+    DWORD StartPosBits = CurrentFlags & AM_SEEKING_PositioningBitsMask;

+

+    if(StopFlags) {

+        CheckPointer(pStop, E_POINTER);

+

+        // accept only relative, incremental, or absolute positioning

+        if(StopPosBits != StopFlags) {

+            return E_INVALIDARG;

+        }

+    }

+

+    if(CurrentFlags) {

+        CheckPointer(pCurrent, E_POINTER);

+        if(StartPosBits != AM_SEEKING_AbsolutePositioning &&

+           StartPosBits != AM_SEEKING_RelativePositioning) {

+            return E_INVALIDARG;

+        }

+    }

+

+

+    // scope for autolock

+    {

+        CAutoLock lock(m_pLock);

+

+        // set start position

+        if(StartPosBits == AM_SEEKING_AbsolutePositioning)

+        {

+            m_rtStart = *pCurrent;

+        }

+        else if(StartPosBits == AM_SEEKING_RelativePositioning)

+        {

+            m_rtStart += *pCurrent;

+        }

+

+        // set stop position

+        if(StopPosBits == AM_SEEKING_AbsolutePositioning)

+        {

+            m_rtStop = *pStop;

+        }

+        else if(StopPosBits == AM_SEEKING_IncrementalPositioning)

+        {

+            m_rtStop = m_rtStart + *pStop;

+        }

+        else if(StopPosBits == AM_SEEKING_RelativePositioning)

+        {

+            m_rtStop = m_rtStop + *pStop;

+        }

+    }

+

+

+    HRESULT hr = S_OK;

+    if(SUCCEEDED(hr) && StopPosBits) {

+        hr = ChangeStop();

+    }

+    if(StartPosBits) {

+        hr = ChangeStart();

+    }

+

+    return hr;

+}

+

+

+HRESULT CSourceSeeking::GetPositions( __out_opt LONGLONG * pCurrent, __out_opt LONGLONG * pStop )

+{

+    if(pCurrent) {

+        *pCurrent = m_rtStart;

+    }

+    if(pStop) {

+        *pStop = m_rtStop;

+    }

+

+    return S_OK;;

+}

+

+

+HRESULT CSourceSeeking::GetAvailable( __out_opt LONGLONG * pEarliest, __out_opt LONGLONG * pLatest )

+{

+    if(pEarliest) {

+        *pEarliest = 0;

+    }

+    if(pLatest) {

+        CAutoLock lock(m_pLock);

+        *pLatest = m_rtDuration;

+    }

+    return S_OK;

+}

+

+HRESULT CSourceSeeking::SetRate( double dRate)

+{

+    {

+        CAutoLock lock(m_pLock);

+        m_dRateSeeking = dRate;

+    }

+    return ChangeRate();

+}

+

+HRESULT CSourceSeeking::GetRate( __out double * pdRate)

+{

+    CheckPointer(pdRate, E_POINTER);

+    CAutoLock lock(m_pLock);

+    *pdRate = m_dRateSeeking;

+    return S_OK;

+}

+

+HRESULT CSourceSeeking::GetPreroll(__out LONGLONG *pPreroll)

+{

+    CheckPointer(pPreroll, E_POINTER);

+    *pPreroll = 0;

+    return S_OK;

+}

+

+

+

+

+

+// --- CSourcePosition implementation ----------

+

+

+CSourcePosition::CSourcePosition(__in_opt LPCTSTR pName,

+				 __in_opt LPUNKNOWN pUnk,

+				 __inout HRESULT* phr,

+				 __in CCritSec * pLock) :

+    CMediaPosition(pName, pUnk),

+    m_pLock(pLock),

+    m_Start(CRefTime((LONGLONG)0))

+{

+    m_Stop = _I64_MAX;

+    m_Rate = 1.0;

+}

+

+

+STDMETHODIMP

+CSourcePosition::get_Duration(__out REFTIME * plength)

+{

+    CheckPointer(plength,E_POINTER);

+    ValidateReadWritePtr(plength,sizeof(REFTIME));

+    CAutoLock lock(m_pLock);

+

+    *plength = m_Duration;

+    return S_OK;

+}

+

+

+STDMETHODIMP

+CSourcePosition::put_CurrentPosition(REFTIME llTime)

+{

+    m_pLock->Lock();

+    m_Start = llTime;

+    m_pLock->Unlock();

+

+    return ChangeStart();

+}

+

+

+STDMETHODIMP

+CSourcePosition::get_StopTime(__out REFTIME * pllTime)

+{

+    CheckPointer(pllTime,E_POINTER);

+    ValidateReadWritePtr(pllTime,sizeof(REFTIME));

+    CAutoLock lock(m_pLock);

+

+    *pllTime = m_Stop;

+    return S_OK;

+}

+

+

+STDMETHODIMP

+CSourcePosition::put_StopTime(REFTIME llTime)

+{

+    m_pLock->Lock();

+    m_Stop = llTime;

+    m_pLock->Unlock();

+

+    return ChangeStop();

+}

+

+

+STDMETHODIMP

+CSourcePosition::get_PrerollTime(__out REFTIME * pllTime)

+{

+    CheckPointer(pllTime,E_POINTER);

+    ValidateReadWritePtr(pllTime,sizeof(REFTIME));

+    return E_NOTIMPL;

+}

+

+

+STDMETHODIMP

+CSourcePosition::put_PrerollTime(REFTIME llTime)

+{

+    return E_NOTIMPL;

+}

+

+

+STDMETHODIMP

+CSourcePosition::get_Rate(__out double * pdRate)

+{

+    CheckPointer(pdRate,E_POINTER);

+    ValidateReadWritePtr(pdRate,sizeof(double));

+    CAutoLock lock(m_pLock);

+

+    *pdRate = m_Rate;

+    return S_OK;

+}

+

+

+STDMETHODIMP

+CSourcePosition::put_Rate(double dRate)

+{

+    m_pLock->Lock();

+    m_Rate = dRate;

+    m_pLock->Unlock();

+

+    return ChangeRate();

+}

+

+

+// By default we can seek forwards

+

+STDMETHODIMP

+CSourcePosition::CanSeekForward(__out LONG *pCanSeekForward)

+{

+    CheckPointer(pCanSeekForward,E_POINTER);

+    *pCanSeekForward = OATRUE;

+    return S_OK;

+}

+

+

+// By default we can seek backwards

+

+STDMETHODIMP

+CSourcePosition::CanSeekBackward(__out LONG *pCanSeekBackward)

+{

+    CheckPointer(pCanSeekBackward,E_POINTER);

+    *pCanSeekBackward = OATRUE;

+    return S_OK;

+}

+

+

+// --- Implementation of CBasicAudio class ----------

+

+

+CBasicAudio::CBasicAudio(__in_opt LPCTSTR pName,__in_opt LPUNKNOWN punk) :

+    CUnknown(pName, punk)

+{

+}

+

+// overriden to publicise our interfaces

+

+STDMETHODIMP

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

+{

+    ValidateReadWritePtr(ppv,sizeof(PVOID));

+    if (riid == IID_IBasicAudio) {

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

+    } else {

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

+    }

+}

+

+

+STDMETHODIMP

+CBasicAudio::GetTypeInfoCount(__out UINT * pctinfo)

+{

+    return m_basedisp.GetTypeInfoCount(pctinfo);

+}

+

+

+STDMETHODIMP

+CBasicAudio::GetTypeInfo(

+  UINT itinfo,

+  LCID lcid,

+  __deref_out ITypeInfo ** pptinfo)

+{

+    return m_basedisp.GetTypeInfo(

+		IID_IBasicAudio,

+		itinfo,

+		lcid,

+		pptinfo);

+}

+

+

+STDMETHODIMP

+CBasicAudio::GetIDsOfNames(

+  REFIID riid,

+  __in_ecount(cNames) LPOLESTR * rgszNames,

+  UINT cNames,

+  LCID lcid,

+  __out_ecount(cNames) DISPID * rgdispid)

+{

+    return m_basedisp.GetIDsOfNames(

+			IID_IBasicAudio,

+			rgszNames,

+			cNames,

+			lcid,

+			rgdispid);

+}

+

+

+STDMETHODIMP

+CBasicAudio::Invoke(

+  DISPID dispidMember,

+  REFIID riid,

+  LCID lcid,

+  WORD wFlags,

+  __in DISPPARAMS * pdispparams,

+  __out_opt VARIANT * pvarResult,

+  __out_opt EXCEPINFO * pexcepinfo,

+  __out_opt UINT * puArgErr)

+{

+    // this parameter is a dead leftover from an earlier interface

+    if (IID_NULL != riid) {

+	return DISP_E_UNKNOWNINTERFACE;

+    }

+

+    ITypeInfo * pti;

+    HRESULT hr = GetTypeInfo(0, lcid, &pti);

+

+    if (FAILED(hr)) {

+	return hr;

+    }

+

+    hr = pti->Invoke(

+	    (IBasicAudio *)this,

+	    dispidMember,

+	    wFlags,

+	    pdispparams,

+	    pvarResult,

+	    pexcepinfo,

+	    puArgErr);

+

+    pti->Release();

+    return hr;

+}

+

+

+// --- IVideoWindow implementation ----------

+

+CBaseVideoWindow::CBaseVideoWindow(__in_opt LPCTSTR pName,__in_opt LPUNKNOWN punk) :

+    CUnknown(pName, punk)

+{

+}

+

+

+// overriden to publicise our interfaces

+

+STDMETHODIMP

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

+{

+    ValidateReadWritePtr(ppv,sizeof(PVOID));

+    if (riid == IID_IVideoWindow) {

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

+    } else {

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

+    }

+}

+

+

+STDMETHODIMP

+CBaseVideoWindow::GetTypeInfoCount(__out UINT * pctinfo)

+{

+    return m_basedisp.GetTypeInfoCount(pctinfo);

+}

+

+

+STDMETHODIMP

+CBaseVideoWindow::GetTypeInfo(

+  UINT itinfo,

+  LCID lcid,

+  __deref_out ITypeInfo ** pptinfo)

+{

+    return m_basedisp.GetTypeInfo(

+		IID_IVideoWindow,

+		itinfo,

+		lcid,

+		pptinfo);

+}

+

+

+STDMETHODIMP

+CBaseVideoWindow::GetIDsOfNames(

+  REFIID riid,

+  __in_ecount(cNames) LPOLESTR * rgszNames,

+  UINT cNames,

+  LCID lcid,

+  __out_ecount(cNames) DISPID * rgdispid)

+{

+    return m_basedisp.GetIDsOfNames(

+			IID_IVideoWindow,

+			rgszNames,

+			cNames,

+			lcid,

+			rgdispid);

+}

+

+

+STDMETHODIMP

+CBaseVideoWindow::Invoke(

+  DISPID dispidMember,

+  REFIID riid,

+  LCID lcid,

+  WORD wFlags,

+  __in DISPPARAMS * pdispparams,

+  __out_opt VARIANT * pvarResult,

+  __out_opt EXCEPINFO * pexcepinfo,

+  __out_opt UINT * puArgErr)

+{

+    // this parameter is a dead leftover from an earlier interface

+    if (IID_NULL != riid) {

+	return DISP_E_UNKNOWNINTERFACE;

+    }

+

+    ITypeInfo * pti;

+    HRESULT hr = GetTypeInfo(0, lcid, &pti);

+

+    if (FAILED(hr)) {

+	return hr;

+    }

+

+    hr = pti->Invoke(

+	    (IVideoWindow *)this,

+	    dispidMember,

+	    wFlags,

+	    pdispparams,

+	    pvarResult,

+	    pexcepinfo,

+	    puArgErr);

+

+    pti->Release();

+    return hr;

+}

+

+

+// --- IBasicVideo implementation ----------

+

+

+CBaseBasicVideo::CBaseBasicVideo(__in_opt LPCTSTR pName,__in_opt LPUNKNOWN punk) :

+    CUnknown(pName, punk)

+{

+}

+

+

+// overriden to publicise our interfaces

+

+STDMETHODIMP

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

+{

+    ValidateReadWritePtr(ppv,sizeof(PVOID));

+    if (riid == IID_IBasicVideo || riid == IID_IBasicVideo2) {

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

+    } else {

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

+    }

+}

+

+

+STDMETHODIMP

+CBaseBasicVideo::GetTypeInfoCount(__out UINT * pctinfo)

+{

+    return m_basedisp.GetTypeInfoCount(pctinfo);

+}

+

+

+STDMETHODIMP

+CBaseBasicVideo::GetTypeInfo(

+  UINT itinfo,

+  LCID lcid,

+  __deref_out ITypeInfo ** pptinfo)

+{

+    return m_basedisp.GetTypeInfo(

+		IID_IBasicVideo,

+		itinfo,

+		lcid,

+		pptinfo);

+}

+

+

+STDMETHODIMP

+CBaseBasicVideo::GetIDsOfNames(

+  REFIID riid,

+  __in_ecount(cNames) LPOLESTR * rgszNames,

+  UINT cNames,

+  LCID lcid,

+  __out_ecount(cNames) DISPID * rgdispid)

+{

+    return m_basedisp.GetIDsOfNames(

+			IID_IBasicVideo,

+			rgszNames,

+			cNames,

+			lcid,

+			rgdispid);

+}

+

+

+STDMETHODIMP

+CBaseBasicVideo::Invoke(

+  DISPID dispidMember,

+  REFIID riid,

+  LCID lcid,

+  WORD wFlags,

+  __in DISPPARAMS * pdispparams,

+  __out_opt VARIANT * pvarResult,

+  __out_opt EXCEPINFO * pexcepinfo,

+  __out_opt UINT * puArgErr)

+{

+    // this parameter is a dead leftover from an earlier interface

+    if (IID_NULL != riid) {

+	return DISP_E_UNKNOWNINTERFACE;

+    }

+

+    ITypeInfo * pti;

+    HRESULT hr = GetTypeInfo(0, lcid, &pti);

+

+    if (FAILED(hr)) {

+	return hr;

+    }

+

+    hr = pti->Invoke(

+	    (IBasicVideo *)this,

+	    dispidMember,

+	    wFlags,

+	    pdispparams,

+	    pvarResult,

+	    pexcepinfo,

+	    puArgErr);

+

+    pti->Release();

+    return hr;

+}

+

+

+// --- Implementation of Deferred Commands ----------

+

+

+CDispParams::CDispParams(UINT nArgs, __in_ecount(nArgs) VARIANT* pArgs, __inout_opt HRESULT *phr)

+{

+   cNamedArgs = 0;

+   rgdispidNamedArgs = NULL;

+   cArgs = nArgs;

+

+    if (cArgs) {

+	rgvarg = new VARIANT[cArgs];

+        if (NULL == rgvarg) {

+            cArgs = 0;

+            if (phr) {

+                *phr = E_OUTOFMEMORY;

+            }

+            return;

+        }

+

+	for (UINT i = 0; i < cArgs; i++) {

+

+            //  Why aren't we using VariantCopy?

+

+	    VARIANT * pDest = &rgvarg[i];

+	    VARIANT * pSrc = &pArgs[i];

+

+	    pDest->vt = pSrc->vt;

+	    switch(pDest->vt) {

+

+	    case VT_I4:

+		pDest->lVal = pSrc->lVal;

+		break;

+

+	    case VT_UI1:

+		pDest->bVal = pSrc->bVal;

+		break;

+

+	    case VT_I2:

+		pDest->iVal = pSrc->iVal;

+		break;

+

+	    case VT_R4:

+		pDest->fltVal = pSrc->fltVal;

+		break;

+

+	    case VT_R8:

+		pDest->dblVal = pSrc->dblVal;

+		break;

+

+	    case VT_BOOL:

+		pDest->boolVal = pSrc->boolVal;

+		break;

+

+	    case VT_ERROR:

+		pDest->scode = pSrc->scode;

+		break;

+

+	    case VT_CY:

+		pDest->cyVal = pSrc->cyVal;

+		break;

+

+	    case VT_DATE:

+		pDest->date = pSrc->date;

+		break;

+

+	    case VT_BSTR:

+		if ((PVOID)pSrc->bstrVal == NULL) {

+		    pDest->bstrVal = NULL;

+		} else {

+

+		    // a BSTR is a WORD followed by a UNICODE string.

+		    // the pointer points just after the WORD

+

+		    WORD len = * (WORD*) (pSrc->bstrVal - (sizeof(WORD) / sizeof(OLECHAR)));

+		    OLECHAR* pch = new OLECHAR[len + (sizeof(WORD)/sizeof(OLECHAR))];

+                    if (pch) {

+        		WORD *pui = (WORD*)pch;

+        		*pui = len;

+         	        pDest->bstrVal = pch + (sizeof(WORD)/sizeof(OLECHAR));

+         		CopyMemory(pDest->bstrVal, pSrc->bstrVal, len*sizeof(OLECHAR));

+                    } else {

+                        cArgs = i;

+                        if (phr) {

+                            *phr = E_OUTOFMEMORY;

+                        }

+                    }

+		}

+		break;

+

+	    case VT_UNKNOWN:

+		pDest->punkVal = pSrc->punkVal;

+		pDest->punkVal->AddRef();

+		break;

+

+	    case VT_DISPATCH:

+		pDest->pdispVal = pSrc->pdispVal;

+		pDest->pdispVal->AddRef();

+		break;

+

+	    default:

+		// a type we haven't got round to adding yet!

+		ASSERT(0);

+		break;

+	    }

+	}

+

+    } else {

+	rgvarg = NULL;

+    }

+

+}

+

+

+CDispParams::~CDispParams()

+{

+    for (UINT i = 0; i < cArgs; i++) {

+	switch(rgvarg[i].vt) {

+        case VT_BSTR:

+            //  Explicitly cast BSTR to PVOID to tell code scanning tools we really mean to test the pointer

+	    if ((PVOID)rgvarg[i].bstrVal != NULL) {

+		OLECHAR * pch = rgvarg[i].bstrVal - (sizeof(WORD)/sizeof(OLECHAR));

+		delete pch;

+	    }

+	    break;

+

+	case VT_UNKNOWN:

+	    rgvarg[i].punkVal->Release();

+	    break;

+

+	case VT_DISPATCH:

+	    rgvarg[i].pdispVal->Release();

+	    break;

+	}

+    }

+    delete[] rgvarg;

+}

+

+

+// lifetime is controlled by refcounts (see defer.h)

+

+CDeferredCommand::CDeferredCommand(

+    __inout CCmdQueue * pQ,

+    __in_opt LPUNKNOWN	pUnk,

+    __inout HRESULT *	phr,

+    __in LPUNKNOWN	pUnkExecutor,

+    REFTIME	time,

+    __in GUID*	iid,

+    long	dispidMethod,

+    short	wFlags,

+    long	nArgs,

+    __in_ecount(nArgs) VARIANT*	pDispParams,

+    __out VARIANT*	pvarResult,

+    __out short*	puArgErr,

+    BOOL	bStream

+    ) :

+	CUnknown(NAME("DeferredCommand"), pUnk),

+	m_pQueue(pQ),

+	m_pUnk(pUnkExecutor),

+	m_iid(iid),

+	m_dispidMethod(dispidMethod),

+	m_wFlags(wFlags),

+	m_DispParams(nArgs, pDispParams, phr),

+	m_pvarResult(pvarResult),

+	m_bStream(bStream),

+	m_hrResult(E_ABORT)

+

+{

+    // convert REFTIME to REFERENCE_TIME

+    COARefTime convertor(time);

+    m_time = convertor;

+

+    // no check of time validity - it's ok to queue a command that's

+    // already late

+

+    // check iid is supportable on pUnk by QueryInterface for it

+    IUnknown * pInterface;

+    HRESULT hr = m_pUnk->QueryInterface(GetIID(), (void**) &pInterface);

+    if (FAILED(hr)) {

+	*phr = hr;

+	return;

+    }

+    pInterface->Release();

+

+

+    // !!! check dispidMethod and param/return types using typelib

+    ITypeInfo *pti;

+    hr = m_Dispatch.GetTypeInfo(*iid, 0, 0, &pti);

+    if (FAILED(hr)) {

+	*phr = hr;

+	return;

+    }

+    // !!! some sort of ITypeInfo validity check here

+    pti->Release();

+

+

+    // Fix up the dispid for put and get

+    if (wFlags == DISPATCH_PROPERTYPUT) {

+        m_DispParams.cNamedArgs = 1;

+        m_DispId = DISPID_PROPERTYPUT;

+        m_DispParams.rgdispidNamedArgs = &m_DispId;

+    }

+

+    // all checks ok - add to queue

+    hr = pQ->Insert(this);

+    if (FAILED(hr)) {

+	*phr = hr;

+    }

+}

+

+

+// refcounts are held by caller of InvokeAt... and by list. So if

+// we get here, we can't be on the list

+

+#if 0

+CDeferredCommand::~CDeferredCommand()

+{

+    // this assert is invalid since if the queue is deleted while we are

+    // still on the queue, we will have been removed by the queue and this

+    // m_pQueue will not have been modified.

+    // ASSERT(m_pQueue == NULL);

+

+    // we don't hold a ref count on pUnk, which is the object that should

+    // execute the command.

+    // This is because there would otherwise be a circular refcount problem

+    // since pUnk probably owns the CmdQueue object that has a refcount

+    // on us.

+    // The lifetime of pUnk is guaranteed by it being part of, or lifetime

+    // controlled by, our parent object. As long as we are on the list, pUnk

+    // must be valid. Once we are off the list, we do not use pUnk.

+

+}

+#endif

+

+

+// overriden to publicise our interfaces

+

+STDMETHODIMP

+CDeferredCommand::NonDelegatingQueryInterface(REFIID riid, __out void **ppv)

+{

+    ValidateReadWritePtr(ppv,sizeof(PVOID));

+    if (riid == IID_IDeferredCommand) {

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

+    } else {

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

+    }

+}

+

+

+// remove from q. this will reduce the refcount by one (since the q

+// holds a count) but can't make us go away since he must have a

+// refcount in order to call this method.

+

+STDMETHODIMP

+CDeferredCommand::Cancel()

+{

+    if (m_pQueue == NULL) {

+	return VFW_E_ALREADY_CANCELLED;

+    }

+

+    HRESULT hr = m_pQueue->Remove(this);

+    if (FAILED(hr)) {

+	return hr;

+    }

+

+    m_pQueue = NULL;

+    return S_OK;

+}

+

+

+STDMETHODIMP

+CDeferredCommand::Confidence(__out LONG* pConfidence)

+{

+    return E_NOTIMPL;

+}

+

+

+STDMETHODIMP

+CDeferredCommand::GetHResult(__out HRESULT * phrResult)

+{

+    CheckPointer(phrResult,E_POINTER);

+    ValidateReadWritePtr(phrResult,sizeof(HRESULT));

+

+    if (m_pQueue != NULL) {

+	return E_ABORT;

+    }

+    *phrResult = m_hrResult;

+    return S_OK;

+}

+

+

+// set the time to be a new time (checking that it is valid) and

+// then requeue

+

+STDMETHODIMP

+CDeferredCommand::Postpone(REFTIME newtime)

+{

+

+    // check that this time is not past

+    // convert REFTIME to REFERENCE_TIME

+    COARefTime convertor(newtime);

+

+    // check that the time has not passed

+    if (m_pQueue->CheckTime(convertor, IsStreamTime())) {

+	return VFW_E_TIME_ALREADY_PASSED;

+    }

+

+    // extract from list

+    HRESULT hr = m_pQueue->Remove(this);

+    if (FAILED(hr)) {

+	return hr;

+    }

+

+    // change time

+    m_time = convertor;

+

+    // requeue

+    hr = m_pQueue->Insert(this);

+

+    return hr;

+}

+

+

+HRESULT

+CDeferredCommand::Invoke()

+{

+    // check that we are still outstanding

+    if (m_pQueue == NULL) {

+	return VFW_E_ALREADY_CANCELLED;

+    }

+

+    // get the type info

+    ITypeInfo* pti;

+    HRESULT hr = m_Dispatch.GetTypeInfo(GetIID(), 0, 0, &pti);

+    if (FAILED(hr)) {

+	return hr;

+    }

+

+    // qi for the expected interface and then invoke it. Note that we have to

+    // treat the returned interface as IUnknown since we don't know its type.

+    IUnknown* pInterface;

+

+    hr = m_pUnk->QueryInterface(GetIID(), (void**) &pInterface);

+    if (FAILED(hr)) {

+	pti->Release();

+	return hr;

+    }

+

+    EXCEPINFO expinfo;

+    UINT uArgErr;

+    m_hrResult = pti->Invoke(

+	pInterface,

+	GetMethod(),

+	GetFlags(),

+	GetParams(),

+	GetResult(),

+	&expinfo,

+	&uArgErr);

+

+    // release the interface we QI'd for

+    pInterface->Release();

+    pti->Release();

+

+

+    // remove from list whether or not successful

+    // or we loop indefinitely

+    hr = m_pQueue->Remove(this);

+    m_pQueue = NULL;

+    return hr;

+}

+

+

+

+// --- CCmdQueue methods ----------

+

+

+CCmdQueue::CCmdQueue(__inout_opt HRESULT *phr) :

+    m_listPresentation(NAME("Presentation time command list")),

+    m_listStream(NAME("Stream time command list")),

+    m_evDue(TRUE, phr),    // manual reset

+    m_dwAdvise(0),

+    m_pClock(NULL),

+    m_bRunning(FALSE)

+{

+}

+

+

+CCmdQueue::~CCmdQueue()

+{

+    // empty all our lists

+

+    // we hold a refcount on each, so traverse and Release each

+    // entry then RemoveAll to empty the list

+    POSITION pos = m_listPresentation.GetHeadPosition();

+

+    while(pos) {

+	CDeferredCommand* pCmd = m_listPresentation.GetNext(pos);

+	pCmd->Release();

+    }

+    m_listPresentation.RemoveAll();

+

+    pos = m_listStream.GetHeadPosition();

+

+    while(pos) {

+	CDeferredCommand* pCmd = m_listStream.GetNext(pos);

+	pCmd->Release();

+    }

+    m_listStream.RemoveAll();

+

+    if (m_pClock) {

+	if (m_dwAdvise) {

+	    m_pClock->Unadvise(m_dwAdvise);

+	    m_dwAdvise = 0;

+	}

+	m_pClock->Release();

+    }

+}

+

+

+// returns a new CDeferredCommand object that will be initialised with

+// the parameters and will be added to the queue during construction.

+// returns S_OK if successfully created otherwise an error and

+// no object has been queued.

+

+HRESULT

+CCmdQueue::New(

+    __out CDeferredCommand **ppCmd,

+    __in     LPUNKNOWN	pUnk,		// this object will execute command

+    REFTIME	time,

+    __in GUID*	iid,

+    long	dispidMethod,

+    short	wFlags,

+    long	cArgs,

+    __in_ecount(cArgs) VARIANT*	pDispParams,

+    __out VARIANT*	pvarResult,

+    __out short*	puArgErr,

+    BOOL	bStream

+)

+{

+    CAutoLock lock(&m_Lock);

+

+    HRESULT hr = S_OK;

+    *ppCmd = NULL;

+

+    CDeferredCommand* pCmd;

+    pCmd = new CDeferredCommand(

+		    this,

+		    NULL,	    // not aggregated

+		    &hr,

+		    pUnk,	    // this guy will execute

+		    time,

+		    iid,

+		    dispidMethod,

+		    wFlags,

+		    cArgs,

+		    pDispParams,

+		    pvarResult,

+		    puArgErr,

+		    bStream);

+

+    if (pCmd == NULL) {

+	hr = E_OUTOFMEMORY;

+    } else {

+	*ppCmd = pCmd;

+    }

+    return hr;

+}

+

+

+HRESULT

+CCmdQueue::Insert(__in CDeferredCommand* pCmd)

+{

+    CAutoLock lock(&m_Lock);

+

+    // addref the item

+    pCmd->AddRef();

+

+    CGenericList<CDeferredCommand> * pList;

+    if (pCmd->IsStreamTime()) {

+	pList = &m_listStream;

+    } else {

+	pList = &m_listPresentation;

+    }

+    POSITION pos = pList->GetHeadPosition();

+

+    // seek past all items that are before us

+    while (pos &&

+	(pList->GetValid(pos)->GetTime() <= pCmd->GetTime())) {

+

+	pList->GetNext(pos);

+    }

+

+    // now at end of list or in front of items that come later

+    if (!pos) {

+	pList->AddTail(pCmd);

+    } else {

+	pList->AddBefore(pos, pCmd);

+    }

+

+    SetTimeAdvise();

+    return S_OK;

+}

+

+

+HRESULT

+CCmdQueue::Remove(__in CDeferredCommand* pCmd)

+{

+    CAutoLock lock(&m_Lock);

+    HRESULT hr = S_OK;

+

+    CGenericList<CDeferredCommand> * pList;

+    if (pCmd->IsStreamTime()) {

+	pList = &m_listStream;

+    } else {

+	pList = &m_listPresentation;

+    }

+    POSITION pos = pList->GetHeadPosition();

+

+    // traverse the list

+    while (pos && (pList->GetValid(pos) != pCmd)) {

+	pList->GetNext(pos);

+    }

+

+    // did we drop off the end?

+    if (!pos) {

+	hr = VFW_E_NOT_FOUND;

+    } else {

+

+	// found it - now take off list

+	pList->Remove(pos);

+

+	// Insert did an AddRef, so release it

+	pCmd->Release();

+

+	// check that timer request is still for earliest time

+	SetTimeAdvise();

+    }

+    return hr;

+}

+

+

+// set the clock used for timing

+

+HRESULT

+CCmdQueue::SetSyncSource(__in_opt IReferenceClock* pClock)

+{

+    CAutoLock lock(&m_Lock);

+

+    // addref the new clock first in case they are the same

+    if (pClock) {

+	pClock->AddRef();

+    }

+

+    // kill any advise on the old clock

+    if (m_pClock) {

+	if (m_dwAdvise) {

+	    m_pClock->Unadvise(m_dwAdvise);

+	    m_dwAdvise = 0;

+	}

+	m_pClock->Release();

+    }

+    m_pClock = pClock;

+

+    // set up a new advise

+    SetTimeAdvise();

+    return S_OK;

+}

+

+

+// set up a timer event with the reference clock

+

+void

+CCmdQueue::SetTimeAdvise(void)

+{

+    // make sure we have a clock to use

+    if (!m_pClock) {

+	return;

+    }

+

+    // reset the event whenever we are requesting a new signal

+    m_evDue.Reset();

+

+    // time 0 is earliest

+    CRefTime current;

+

+    // find the earliest presentation time

+    POSITION pos = m_listPresentation.GetHeadPosition();

+    if (pos != NULL) {

+	current = m_listPresentation.GetValid(pos)->GetTime();

+    }

+

+    // if we're running, check the stream times too

+    if (m_bRunning) {

+

+	CRefTime t;

+        pos = m_listStream.GetHeadPosition();

+	if (NULL != pos) {

+	    t = m_listStream.GetValid(pos)->GetTime();

+

+	    // add on stream time offset to get presentation time

+	    t += m_StreamTimeOffset;

+

+	    // is this earlier?

+	    if ((current == TimeZero) || (t < current)) {

+		current = t;

+	    }

+	}

+    }

+

+    // need to change?

+    if ((current > TimeZero) && (current != m_tCurrentAdvise)) {

+	if (m_dwAdvise) {

+	    m_pClock->Unadvise(m_dwAdvise);

+	    // reset the event whenever we are requesting a new signal

+	    m_evDue.Reset();

+	}

+

+	// ask for time advice - the first two params are either

+	// stream time offset and stream time or

+	// presentation time and 0. we always use the latter

+	HRESULT hr = m_pClock->AdviseTime(

+		    (REFERENCE_TIME)current,

+		    TimeZero,

+		    (HEVENT) HANDLE(m_evDue),

+		    &m_dwAdvise);

+

+	ASSERT(SUCCEEDED(hr));

+	m_tCurrentAdvise = current;

+    }

+}

+

+

+// switch to run mode. Streamtime to Presentation time mapping known.

+

+HRESULT

+CCmdQueue::Run(REFERENCE_TIME tStreamTimeOffset)

+{

+    CAutoLock lock(&m_Lock);

+

+    m_StreamTimeOffset = tStreamTimeOffset;

+    m_bRunning = TRUE;

+

+    // ensure advise is accurate

+    SetTimeAdvise();

+    return S_OK;

+}

+

+

+// switch to Stopped or Paused mode. Time mapping not known.

+

+HRESULT

+CCmdQueue::EndRun()

+{

+    CAutoLock lock(&m_Lock);

+

+    m_bRunning = FALSE;

+

+    // check timer setting - stream times

+    SetTimeAdvise();

+    return S_OK;

+}

+

+

+// return a pointer to the next due command. Blocks for msTimeout

+// milliseconds until there is a due command.

+// Stream-time commands will only become due between Run and Endrun calls.

+// The command remains queued until invoked or cancelled.

+// Returns E_ABORT if timeout occurs, otherwise S_OK (or other error).

+//

+// returns an AddRef'd object

+

+HRESULT

+CCmdQueue::GetDueCommand(__out CDeferredCommand ** ppCmd, long msTimeout)

+{

+    // loop until we timeout or find a due command

+    for (;;) {

+

+	{

+	    CAutoLock lock(&m_Lock);

+

+

+	    // find the earliest command

+	    CDeferredCommand * pCmd = NULL;

+

+	    // check the presentation time and the

+	    // stream time list to find the earliest

+

+            POSITION pos = m_listPresentation.GetHeadPosition();

+

+	    if (NULL != pos) {

+		pCmd = m_listPresentation.GetValid(pos);

+	    }

+

+	    if (m_bRunning) {

+		pos = m_listStream.GetHeadPosition();

+                if (NULL != pos) {

+                    CDeferredCommand* pStrm = m_listStream.GetValid(pos);

+

+                    CRefTime t = pStrm->GetTime() + m_StreamTimeOffset;

+                    if (!pCmd || (t < pCmd->GetTime())) {

+                        pCmd = pStrm;

+                    }

+                }

+            }

+

+	    //	if we have found one, is it due?

+	    if (pCmd) {

+		if (CheckTime(pCmd->GetTime(), pCmd->IsStreamTime())) {

+

+		    // yes it's due - addref it

+		    pCmd->AddRef();

+		    *ppCmd = pCmd;

+		    return S_OK;

+		}

+	    }

+	}

+

+	// block until the advise is signalled

+	if (WaitForSingleObject(m_evDue, msTimeout) != WAIT_OBJECT_0) {

+	    return E_ABORT;

+	}

+    }

+}

+

+

+// return a pointer to a command that will be due for a given time.

+// Pass in a stream time here. The stream time offset will be passed

+// in via the Run method.

+// Commands remain queued until invoked or cancelled.

+// This method will not block. It will report E_ABORT if there are no

+// commands due yet.

+//

+// returns an AddRef'd object

+

+HRESULT

+CCmdQueue::GetCommandDueFor(REFERENCE_TIME rtStream, __out CDeferredCommand**ppCmd)

+{

+    CAutoLock lock(&m_Lock);

+

+    CRefTime tStream(rtStream);

+

+    // find the earliest stream and presentation time commands

+    CDeferredCommand* pStream = NULL;

+    POSITION pos = m_listStream.GetHeadPosition();

+    if (NULL != pos) {

+	pStream = m_listStream.GetValid(pos);

+    }

+    CDeferredCommand* pPresent = NULL;

+    pos = m_listPresentation.GetHeadPosition();

+    if (NULL != pos) {

+	pPresent = m_listPresentation.GetValid(pos);

+    }

+

+    // is there a presentation time that has passed already

+    if (pPresent && CheckTime(pPresent->GetTime(), FALSE)) {

+	pPresent->AddRef();

+	*ppCmd = pPresent;

+	return S_OK;

+    }

+

+    // is there a stream time command due before this stream time

+    if (pStream && (pStream->GetTime() <= tStream)) {

+	pStream->AddRef();

+	*ppCmd = pStream;

+	return S_OK;

+    }

+

+    // if we are running, we can map presentation times to

+    // stream time. In this case, is there a presentation time command

+    // that will be due before this stream time is presented?

+    if (m_bRunning && pPresent) {

+

+	// this stream time will appear at...

+	tStream += m_StreamTimeOffset;

+

+	// due before that?

+	if (pPresent->GetTime() <= tStream) {

+	    *ppCmd = pPresent;

+	    return S_OK;

+	}

+    }

+

+    // no commands due yet

+    return VFW_E_NOT_FOUND;

+}

+

+#endif /* PJMEDIA_VIDEO_DEV_HAS_DSHOW */

diff --git a/jni/pjproject-android/third_party/BaseClasses/ctlutil.h b/jni/pjproject-android/third_party/BaseClasses/ctlutil.h
new file mode 100644
index 0000000..7e4719c
--- /dev/null
+++ b/jni/pjproject-android/third_party/BaseClasses/ctlutil.h
@@ -0,0 +1,923 @@
+//------------------------------------------------------------------------------

+// File: CtlUtil.h

+//

+// Desc: DirectShow base classes.

+//

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

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

+

+

+// Base classes implementing IDispatch parsing for the basic control dual

+// interfaces. Derive from these and implement just the custom method and

+// property methods. We also implement CPosPassThru that can be used by

+// renderers and transforms to pass by IMediaPosition and IMediaSeeking

+

+#ifndef __CTLUTIL__

+#define __CTLUTIL__

+

+// OLE Automation has different ideas of TRUE and FALSE

+

+#define OATRUE (-1)

+#define OAFALSE (0)

+

+

+// It's possible that we could replace this class with CreateStdDispatch

+

+class CBaseDispatch

+{

+    ITypeInfo * m_pti;

+

+public:

+

+    CBaseDispatch() : m_pti(NULL) {}

+    ~CBaseDispatch();

+

+    /* IDispatch methods */

+    STDMETHODIMP GetTypeInfoCount(__out UINT * pctinfo);

+

+    STDMETHODIMP GetTypeInfo(

+      REFIID riid,

+      UINT itinfo,

+      LCID lcid,

+      __deref_out ITypeInfo ** pptinfo);

+

+    STDMETHODIMP GetIDsOfNames(

+      REFIID riid,

+      __in_ecount(cNames) LPOLESTR * rgszNames,

+      UINT cNames,

+      LCID lcid,

+      __out_ecount(cNames) DISPID * rgdispid);

+};

+

+

+class AM_NOVTABLE CMediaControl :

+    public IMediaControl,

+    public CUnknown

+{

+    CBaseDispatch m_basedisp;

+

+public:

+

+    CMediaControl(const TCHAR *, LPUNKNOWN);

+

+    DECLARE_IUNKNOWN

+

+    // override this to publicise our interfaces

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

+

+    /* IDispatch methods */

+    STDMETHODIMP GetTypeInfoCount(__out UINT * pctinfo);

+

+    STDMETHODIMP GetTypeInfo(

+      UINT itinfo,

+      LCID lcid,

+      __deref_out ITypeInfo ** pptinfo);

+

+    STDMETHODIMP GetIDsOfNames(

+      REFIID riid,

+      __in_ecount(cNames) LPOLESTR * rgszNames,

+      UINT cNames,

+      LCID lcid,

+      __out_ecount(cNames) DISPID * rgdispid);

+

+    STDMETHODIMP Invoke(

+      DISPID dispidMember,

+      REFIID riid,

+      LCID lcid,

+      WORD wFlags,

+      __in DISPPARAMS * pdispparams,

+      __out_opt VARIANT * pvarResult,

+      __out_opt EXCEPINFO * pexcepinfo,

+      __out_opt UINT * puArgErr);

+};

+

+

+class AM_NOVTABLE CMediaEvent :

+    public IMediaEventEx,

+    public CUnknown

+{

+    CBaseDispatch m_basedisp;

+

+public:

+

+    CMediaEvent(__in_opt LPCTSTR, __in_opt LPUNKNOWN);

+

+    DECLARE_IUNKNOWN

+

+    // override this to publicise our interfaces

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

+

+    /* IDispatch methods */

+    STDMETHODIMP GetTypeInfoCount(__out UINT * pctinfo);

+

+    STDMETHODIMP GetTypeInfo(

+      UINT itinfo,

+      LCID lcid,

+      __deref_out ITypeInfo ** pptinfo);

+

+    STDMETHODIMP GetIDsOfNames(

+      REFIID riid,

+      __in_ecount(cNames) LPOLESTR * rgszNames,

+      UINT cNames,

+      LCID lcid,

+      __out_ecount(cNames) DISPID * rgdispid);

+

+    STDMETHODIMP Invoke(

+      DISPID dispidMember,

+      REFIID riid,

+      LCID lcid,

+      WORD wFlags,

+      __in DISPPARAMS * pdispparams,

+      __out_opt VARIANT * pvarResult,

+      __out_opt EXCEPINFO * pexcepinfo,

+      __out_opt UINT * puArgErr);

+};

+

+

+class AM_NOVTABLE CMediaPosition :

+    public IMediaPosition,

+    public CUnknown

+{

+    CBaseDispatch m_basedisp;

+

+

+public:

+

+    CMediaPosition(__in_opt LPCTSTR, __in_opt LPUNKNOWN);

+    CMediaPosition(__in_opt LPCTSTR, __in_opt LPUNKNOWN, __inout HRESULT *phr);

+

+    DECLARE_IUNKNOWN

+

+    // override this to publicise our interfaces

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

+

+    /* IDispatch methods */

+    STDMETHODIMP GetTypeInfoCount(__out UINT * pctinfo);

+

+    STDMETHODIMP GetTypeInfo(

+      UINT itinfo,

+      LCID lcid,

+      __deref_out ITypeInfo ** pptinfo);

+

+    STDMETHODIMP GetIDsOfNames(

+      REFIID riid,

+      __in_ecount(cNames) LPOLESTR * rgszNames,

+      UINT cNames,

+      LCID lcid,

+      __out_ecount(cNames) DISPID * rgdispid);

+

+    STDMETHODIMP Invoke(

+      DISPID dispidMember,

+      REFIID riid,

+      LCID lcid,

+      WORD wFlags,

+      __in DISPPARAMS * pdispparams,

+      __out_opt VARIANT * pvarResult,

+      __out_opt EXCEPINFO * pexcepinfo,

+      __out_opt UINT * puArgErr);

+

+};

+

+

+// OA-compatibility means that we must use double as the RefTime value,

+// and REFERENCE_TIME (essentially a LONGLONG) within filters.

+// this class converts between the two

+

+class COARefTime : public CRefTime {

+public:

+

+    COARefTime() {

+    };

+

+    COARefTime(CRefTime t)

+        : CRefTime(t)

+    {

+    };

+

+    COARefTime(REFERENCE_TIME t)

+        : CRefTime(t)

+    {

+    };

+

+    COARefTime(double d) {

+        m_time = (LONGLONG) (d * 10000000);

+    };

+

+    operator double() {

+        return double(m_time) / 10000000;

+    };

+

+    operator REFERENCE_TIME() {

+        return m_time;

+    };

+

+    COARefTime& operator=(const double& rd)  {

+        m_time = (LONGLONG) (rd * 10000000);

+        return *this;

+    }

+

+    COARefTime& operator=(const REFERENCE_TIME& rt)  {

+        m_time = rt;

+        return *this;

+    }

+

+    inline BOOL operator==(const COARefTime& rt)

+    {

+        return m_time == rt.m_time;

+    };

+

+    inline BOOL operator!=(const COARefTime& rt)

+    {

+        return m_time != rt.m_time;

+    };

+

+    inline BOOL operator < (const COARefTime& rt)

+    {

+        return m_time < rt.m_time;

+    };

+

+    inline BOOL operator > (const COARefTime& rt)

+    {

+        return m_time > rt.m_time;

+    };

+

+    inline BOOL operator >= (const COARefTime& rt)

+    {

+        return m_time >= rt.m_time;

+    };

+

+    inline BOOL operator <= (const COARefTime& rt)

+    {

+        return m_time <= rt.m_time;

+    };

+

+    inline COARefTime operator+(const COARefTime& rt)

+    {

+        return COARefTime(m_time + rt.m_time);

+    };

+

+    inline COARefTime operator-(const COARefTime& rt)

+    {

+        return COARefTime(m_time - rt.m_time);

+    };

+

+    inline COARefTime operator*(LONG l)

+    {

+        return COARefTime(m_time * l);

+    };

+

+    inline COARefTime operator/(LONG l)

+    {

+        return COARefTime(m_time / l);

+    };

+

+private:

+    //  Prevent bugs from constructing from LONG (which gets

+    //  converted to double and then multiplied by 10000000

+    COARefTime(LONG);

+    LONG operator=(LONG);

+};

+

+

+// A utility class that handles IMediaPosition and IMediaSeeking on behalf

+// of single-input pin renderers, or transform filters.

+//

+// Renderers will expose this from the filter; transform filters will

+// expose it from the output pin and not the renderer.

+//

+// Create one of these, giving it your IPin* for your input pin, and delegate

+// all IMediaPosition methods to it. It will query the input pin for

+// IMediaPosition and respond appropriately.

+//

+// Call ForceRefresh if the pin connection changes.

+//

+// This class no longer caches the upstream IMediaPosition or IMediaSeeking

+// it acquires it on each method call. This means ForceRefresh is not needed.

+// The method is kept for source compatibility and to minimise the changes

+// if we need to put it back later for performance reasons.

+

+class CPosPassThru : public IMediaSeeking, public CMediaPosition

+{

+    IPin *m_pPin;

+

+    HRESULT GetPeer(__deref_out IMediaPosition **ppMP);

+    HRESULT GetPeerSeeking(__deref_out IMediaSeeking **ppMS);

+

+public:

+

+    CPosPassThru(__in_opt LPCTSTR, __in_opt LPUNKNOWN, __inout HRESULT*, IPin *);

+    DECLARE_IUNKNOWN

+

+    HRESULT ForceRefresh() {

+        return S_OK;

+    };

+

+    // override to return an accurate current position

+    virtual HRESULT GetMediaTime(__out LONGLONG *pStartTime, __out_opt LONGLONG *pEndTime) {

+        return E_FAIL;

+    }

+

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

+

+    // IMediaSeeking methods

+    STDMETHODIMP GetCapabilities( __out DWORD * pCapabilities );

+    STDMETHODIMP CheckCapabilities( __inout DWORD * pCapabilities );

+    STDMETHODIMP SetTimeFormat(const GUID * pFormat);

+    STDMETHODIMP GetTimeFormat(__out GUID *pFormat);

+    STDMETHODIMP IsUsingTimeFormat(const GUID * pFormat);

+    STDMETHODIMP IsFormatSupported( const GUID * pFormat);

+    STDMETHODIMP QueryPreferredFormat( __out GUID *pFormat);

+    STDMETHODIMP ConvertTimeFormat(__out LONGLONG * pTarget, 

+                                   __in_opt const GUID * pTargetFormat,

+                                   LONGLONG Source, 

+                                   __in_opt const GUID * pSourceFormat );

+    STDMETHODIMP SetPositions( __inout_opt LONGLONG * pCurrent, DWORD CurrentFlags

+                             , __inout_opt LONGLONG * pStop, DWORD StopFlags );

+

+    STDMETHODIMP GetPositions( __out_opt LONGLONG * pCurrent, __out_opt LONGLONG * pStop );

+    STDMETHODIMP GetCurrentPosition( __out LONGLONG * pCurrent );

+    STDMETHODIMP GetStopPosition( __out LONGLONG * pStop );

+    STDMETHODIMP SetRate( double dRate);

+    STDMETHODIMP GetRate( __out double * pdRate);

+    STDMETHODIMP GetDuration( __out LONGLONG *pDuration);

+    STDMETHODIMP GetAvailable( __out_opt LONGLONG *pEarliest, __out_opt LONGLONG *pLatest );

+    STDMETHODIMP GetPreroll( __out LONGLONG *pllPreroll );

+

+    // IMediaPosition properties

+    STDMETHODIMP get_Duration(__out REFTIME * plength);

+    STDMETHODIMP put_CurrentPosition(REFTIME llTime);

+    STDMETHODIMP get_StopTime(__out REFTIME * pllTime);

+    STDMETHODIMP put_StopTime(REFTIME llTime);

+    STDMETHODIMP get_PrerollTime(__out REFTIME * pllTime);

+    STDMETHODIMP put_PrerollTime(REFTIME llTime);

+    STDMETHODIMP get_Rate(__out double * pdRate);

+    STDMETHODIMP put_Rate(double dRate);

+    STDMETHODIMP get_CurrentPosition(__out REFTIME * pllTime);

+    STDMETHODIMP CanSeekForward(__out LONG *pCanSeekForward);

+    STDMETHODIMP CanSeekBackward(__out LONG *pCanSeekBackward);

+

+private:

+    HRESULT GetSeekingLongLong( HRESULT (__stdcall IMediaSeeking::*pMethod)( LONGLONG * ),

+                                __out LONGLONG * pll );

+};

+

+

+// Adds the ability to return a current position

+

+class CRendererPosPassThru : public CPosPassThru

+{

+    CCritSec m_PositionLock;    // Locks access to our position

+    LONGLONG m_StartMedia;      // Start media time last seen

+    LONGLONG m_EndMedia;        // And likewise the end media

+    BOOL m_bReset;              // Have media times been set

+

+public:

+

+    // Used to help with passing media times through graph

+

+    CRendererPosPassThru(__in_opt LPCTSTR, __in_opt LPUNKNOWN, __inout HRESULT*, IPin *);

+    HRESULT RegisterMediaTime(IMediaSample *pMediaSample);

+    HRESULT RegisterMediaTime(LONGLONG StartTime,LONGLONG EndTime);

+    HRESULT GetMediaTime(__out LONGLONG *pStartTime,__out_opt LONGLONG *pEndTime);

+    HRESULT ResetMediaTime();

+    HRESULT EOS();

+};

+

+STDAPI CreatePosPassThru(

+    __in_opt LPUNKNOWN pAgg,

+    BOOL bRenderer,

+    IPin *pPin,

+    __deref_out IUnknown **ppPassThru

+);

+

+// A class that handles the IDispatch part of IBasicAudio and leaves the

+// properties and methods themselves pure virtual.

+

+class AM_NOVTABLE CBasicAudio : public IBasicAudio, public CUnknown

+{

+    CBaseDispatch m_basedisp;

+

+public:

+

+    CBasicAudio(__in_opt LPCTSTR, __in_opt LPUNKNOWN);

+

+    DECLARE_IUNKNOWN

+

+    // override this to publicise our interfaces

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

+

+    /* IDispatch methods */

+    STDMETHODIMP GetTypeInfoCount(__out UINT * pctinfo);

+

+    STDMETHODIMP GetTypeInfo(

+      UINT itinfo,

+      LCID lcid,

+      __deref_out ITypeInfo ** pptinfo);

+

+    STDMETHODIMP GetIDsOfNames(

+      REFIID riid,

+      __in_ecount(cNames) LPOLESTR * rgszNames,

+      UINT cNames,

+      LCID lcid,

+      __out_ecount(cNames) DISPID * rgdispid);

+

+    STDMETHODIMP Invoke(

+      DISPID dispidMember,

+      REFIID riid,

+      LCID lcid,

+      WORD wFlags,

+      __in DISPPARAMS * pdispparams,

+      __out_opt VARIANT * pvarResult,

+      __out_opt EXCEPINFO * pexcepinfo,

+      __out_opt UINT * puArgErr);

+};

+

+

+// A class that handles the IDispatch part of IBasicVideo and leaves the

+// properties and methods themselves pure virtual.

+

+class AM_NOVTABLE CBaseBasicVideo : public IBasicVideo2, public CUnknown

+{

+    CBaseDispatch m_basedisp;

+

+public:

+

+    CBaseBasicVideo(__in_opt LPCTSTR, __in_opt LPUNKNOWN);

+

+    DECLARE_IUNKNOWN

+

+    // override this to publicise our interfaces

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

+

+    /* IDispatch methods */

+    STDMETHODIMP GetTypeInfoCount(__out UINT * pctinfo);

+

+    STDMETHODIMP GetTypeInfo(

+      UINT itinfo,

+      LCID lcid,

+      __deref_out ITypeInfo ** pptinfo);

+

+    STDMETHODIMP GetIDsOfNames(

+      REFIID riid,

+      __in_ecount(cNames) LPOLESTR * rgszNames,

+      UINT cNames,

+      LCID lcid,

+      __out_ecount(cNames) DISPID * rgdispid);

+

+    STDMETHODIMP Invoke(

+      DISPID dispidMember,

+      REFIID riid,

+      LCID lcid,

+      WORD wFlags,

+      __in DISPPARAMS * pdispparams,

+      __out_opt VARIANT * pvarResult,

+      __out_opt EXCEPINFO * pexcepinfo,

+      __out_opt UINT * puArgErr);

+

+    STDMETHODIMP GetPreferredAspectRatio(

+      __out long *plAspectX,

+      __out long *plAspectY)

+    {

+        return E_NOTIMPL;

+    }

+};

+

+

+// A class that handles the IDispatch part of IVideoWindow and leaves the

+// properties and methods themselves pure virtual.

+

+class AM_NOVTABLE CBaseVideoWindow : public IVideoWindow, public CUnknown

+{

+    CBaseDispatch m_basedisp;

+

+public:

+

+    CBaseVideoWindow(__in_opt LPCTSTR, __in_opt LPUNKNOWN);

+

+    DECLARE_IUNKNOWN

+

+    // override this to publicise our interfaces

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

+

+    /* IDispatch methods */

+    STDMETHODIMP GetTypeInfoCount(__out UINT * pctinfo);

+

+    STDMETHODIMP GetTypeInfo(

+      UINT itinfo,

+      LCID lcid,

+      __deref_out ITypeInfo ** pptinfo);

+

+    STDMETHODIMP GetIDsOfNames(

+      REFIID riid,

+      __in_ecount(cNames) LPOLESTR * rgszNames,

+      UINT cNames,

+      LCID lcid,

+      __out_ecount(cNames) DISPID * rgdispid);

+

+    STDMETHODIMP Invoke(

+      DISPID dispidMember,

+      REFIID riid,

+      LCID lcid,

+      WORD wFlags,

+      __in DISPPARAMS * pdispparams,

+      __out_opt VARIANT * pvarResult,

+      __out_opt EXCEPINFO * pexcepinfo,

+      __out_opt UINT * puArgErr);

+};

+

+

+// abstract class to help source filters with their implementation

+// of IMediaPosition. Derive from this and set the duration (and stop

+// position). Also override NotifyChange to do something when the properties

+// change.

+

+class AM_NOVTABLE CSourcePosition : public CMediaPosition

+{

+

+public:

+    CSourcePosition(__in_opt LPCTSTR, __in_opt LPUNKNOWN, __inout HRESULT*, __in CCritSec *);

+

+    // IMediaPosition methods

+    STDMETHODIMP get_Duration(__out REFTIME * plength);

+    STDMETHODIMP put_CurrentPosition(REFTIME llTime);

+    STDMETHODIMP get_StopTime(__out REFTIME * pllTime);

+    STDMETHODIMP put_StopTime(REFTIME llTime);

+    STDMETHODIMP get_PrerollTime(__out REFTIME * pllTime);

+    STDMETHODIMP put_PrerollTime(REFTIME llTime);

+    STDMETHODIMP get_Rate(__out double * pdRate);

+    STDMETHODIMP put_Rate(double dRate);

+    STDMETHODIMP CanSeekForward(__out LONG *pCanSeekForward);

+    STDMETHODIMP CanSeekBackward(__out LONG *pCanSeekBackward);

+

+    // override if you can return the data you are actually working on

+    STDMETHODIMP get_CurrentPosition(__out REFTIME * pllTime) {

+        return E_NOTIMPL;

+    };

+

+protected:

+

+    // we call this to notify changes. Override to handle them

+    virtual HRESULT ChangeStart() PURE;

+    virtual HRESULT ChangeStop() PURE;

+    virtual HRESULT ChangeRate() PURE;

+

+    COARefTime m_Duration;

+    COARefTime m_Start;

+    COARefTime m_Stop;

+    double m_Rate;

+

+    CCritSec * m_pLock;

+};

+

+class AM_NOVTABLE CSourceSeeking :

+    public IMediaSeeking,

+    public CUnknown

+{

+

+public:

+

+    DECLARE_IUNKNOWN;

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

+

+    // IMediaSeeking methods

+

+    STDMETHODIMP IsFormatSupported(const GUID * pFormat);

+    STDMETHODIMP QueryPreferredFormat(__out GUID *pFormat);

+    STDMETHODIMP SetTimeFormat(const GUID * pFormat);

+    STDMETHODIMP IsUsingTimeFormat(const GUID * pFormat);

+    STDMETHODIMP GetTimeFormat(__out GUID *pFormat);

+    STDMETHODIMP GetDuration(__out LONGLONG *pDuration);

+    STDMETHODIMP GetStopPosition(__out LONGLONG *pStop);

+    STDMETHODIMP GetCurrentPosition(__out LONGLONG *pCurrent);

+    STDMETHODIMP GetCapabilities( __out DWORD * pCapabilities );

+    STDMETHODIMP CheckCapabilities( __inout DWORD * pCapabilities );

+    STDMETHODIMP ConvertTimeFormat( __out LONGLONG * pTarget, 

+                                    __in_opt const GUID * pTargetFormat,

+                                    LONGLONG Source, 

+                                    __in_opt const GUID * pSourceFormat );

+

+    STDMETHODIMP SetPositions( __inout_opt LONGLONG * pCurrent,  DWORD CurrentFlags

+			     , __inout_opt LONGLONG * pStop,  DWORD StopFlags );

+

+    STDMETHODIMP GetPositions( __out_opt LONGLONG * pCurrent, __out_opt LONGLONG * pStop );

+

+    STDMETHODIMP GetAvailable( __out_opt LONGLONG * pEarliest, __out_opt LONGLONG * pLatest );

+    STDMETHODIMP SetRate( double dRate);

+    STDMETHODIMP GetRate( __out double * pdRate);

+    STDMETHODIMP GetPreroll(__out LONGLONG *pPreroll);

+

+

+protected:

+

+    // ctor

+    CSourceSeeking(__in_opt LPCTSTR, __in_opt LPUNKNOWN, __inout HRESULT*, __in CCritSec *);

+

+    // we call this to notify changes. Override to handle them

+    virtual HRESULT ChangeStart() PURE;

+    virtual HRESULT ChangeStop() PURE;

+    virtual HRESULT ChangeRate() PURE;

+

+    CRefTime m_rtDuration;      // length of stream

+    CRefTime m_rtStart;         // source will start here

+    CRefTime m_rtStop;          // source will stop here

+    double m_dRateSeeking;

+

+    // seeking capabilities

+    DWORD m_dwSeekingCaps;

+

+    CCritSec * m_pLock;

+};

+

+

+// Base classes supporting Deferred commands.

+

+// Deferred commands are queued by calls to methods on the IQueueCommand

+// interface, exposed by the filtergraph and by some filters. A successful

+// call to one of these methods will return an IDeferredCommand interface

+// representing the queued command.

+//

+// A CDeferredCommand object represents a single deferred command, and exposes

+// the IDeferredCommand interface as well as other methods permitting time

+// checks and actual execution. It contains a reference to the CCommandQueue

+// object on which it is queued.

+//

+// CCommandQueue is a base class providing a queue of CDeferredCommand

+// objects, and methods to add, remove, check status and invoke the queued

+// commands. A CCommandQueue object would be part of an object that

+// implemented IQueueCommand.

+

+class CCmdQueue;

+

+// take a copy of the params and store them. Release any allocated

+// memory in destructor

+

+class CDispParams : public DISPPARAMS

+{

+public:

+    CDispParams(UINT nArgs, __in_ecount(nArgs) VARIANT* pArgs, __inout_opt HRESULT *phr = NULL);

+    ~CDispParams();

+};

+

+

+// CDeferredCommand lifetime is controlled by refcounts. Caller of

+// InvokeAt.. gets a refcounted interface pointer, and the CCmdQueue

+// object also holds a refcount on us. Calling Cancel or Invoke takes

+// us off the CCmdQueue and thus reduces the refcount by 1. Once taken

+// off the queue we cannot be put back on the queue.

+

+class CDeferredCommand

+    : public CUnknown,

+      public IDeferredCommand

+{

+public:

+

+    CDeferredCommand(

+        __inout CCmdQueue * pQ,

+        __in_opt LPUNKNOWN   pUnk,               // aggregation outer unk

+        __inout HRESULT *   phr,

+        __in LPUNKNOWN   pUnkExecutor,       // object that will execute this cmd

+        REFTIME     time,

+        __in GUID*       iid,

+        long        dispidMethod,

+        short       wFlags,

+        long        cArgs,

+        __in_ecount(cArgs) VARIANT*    pDispParams,

+        __out VARIANT*    pvarResult,

+        __out short*      puArgErr,

+        BOOL        bStream

+        );

+

+    DECLARE_IUNKNOWN

+

+    // override this to publicise our interfaces

+    STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, __out void **ppv);

+

+    // IDeferredCommand methods

+    STDMETHODIMP Cancel();

+    STDMETHODIMP Confidence(

+                    __out LONG* pConfidence);

+    STDMETHODIMP Postpone(

+                    REFTIME newtime);

+    STDMETHODIMP GetHResult(

+                    __out HRESULT* phrResult);

+

+    // other public methods

+

+    HRESULT Invoke();

+

+    // access methods

+

+    // returns TRUE if streamtime, FALSE if presentation time

+    BOOL IsStreamTime() {

+       return m_bStream;

+    };

+

+    CRefTime GetTime() {

+        return m_time;

+    };

+

+    REFIID GetIID() {

+        return *m_iid;

+    };

+

+    long GetMethod() {

+        return m_dispidMethod;

+    };

+

+    short GetFlags() {

+        return m_wFlags;

+    };

+

+    DISPPARAMS* GetParams() {

+        return &m_DispParams;

+    };

+

+    VARIANT* GetResult() {

+        return m_pvarResult;

+    };

+

+protected:

+

+    CCmdQueue* m_pQueue;

+

+    // pUnk for the interface that we will execute the command on

+    LPUNKNOWN   m_pUnk;

+

+    // stored command data

+    REFERENCE_TIME     m_time;

+    GUID*       m_iid;

+    long        m_dispidMethod;

+    short       m_wFlags;

+    VARIANT*    m_pvarResult;

+    BOOL        m_bStream;

+    CDispParams m_DispParams;

+    DISPID      m_DispId;         //  For get and put

+

+    // we use this for ITypeInfo access

+    CBaseDispatch   m_Dispatch;

+

+    // save retval here

+    HRESULT     m_hrResult;

+};

+

+

+// a list of CDeferredCommand objects. this is a base class providing

+// the basics of access to the list. If you want to use CDeferredCommand

+// objects then your queue needs to be derived from this class.

+

+class AM_NOVTABLE CCmdQueue

+{

+public:

+    CCmdQueue(__inout_opt HRESULT *phr = NULL);

+    virtual ~CCmdQueue();

+

+    // returns a new CDeferredCommand object that will be initialised with

+    // the parameters and will be added to the queue during construction.

+    // returns S_OK if successfully created otherwise an error and

+    // no object has been queued.

+    virtual HRESULT  New(

+        __out CDeferredCommand **ppCmd,

+        __in LPUNKNOWN   pUnk,

+        REFTIME     time,

+        __in GUID*       iid,

+        long        dispidMethod,

+        short       wFlags,

+        long        cArgs,

+        __in_ecount(cArgs) VARIANT*    pDispParams,

+        __out VARIANT*    pvarResult,

+        __out short*      puArgErr,

+        BOOL        bStream

+    );

+

+    // called by the CDeferredCommand object to add and remove itself

+    // from the queue

+    virtual HRESULT Insert(__in CDeferredCommand* pCmd);

+    virtual HRESULT Remove(__in CDeferredCommand* pCmd);

+

+    // Command-Due Checking

+    //

+    // There are two schemes of synchronisation: coarse and accurate. In

+    // coarse mode, you wait till the time arrives and then execute the cmd.

+    // In accurate mode, you wait until you are processing the sample that

+    // will appear at the time, and then execute the command. It's up to the

+    // filter which one it will implement. The filtergraph will always

+    // implement coarse mode for commands queued at the filtergraph.

+    //

+    // If you want coarse sync, you probably want to wait until there is a

+    // command due, and then execute it. You can do this by calling

+    // GetDueCommand. If you have several things to wait for, get the

+    // event handle from GetDueHandle() and when this is signalled then call

+    // GetDueCommand. Stream time will only advance between calls to Run and

+    // EndRun. Note that to avoid an extra thread there is no guarantee that

+    // if the handle is set there will be a command ready. Each time the

+    // event is signalled, call GetDueCommand (probably with a 0 timeout);

+    // This may return E_ABORT.

+    //

+    // If you want accurate sync, you must call GetCommandDueFor, passing

+    // as a parameter the stream time of the samples you are about to process.

+    // This will return:

+    //   -- a stream-time command due at or before that stream time

+    //   -- a presentation-time command due at or before the

+    //      time that stream time will be presented (only between Run

+    //      and EndRun calls, since outside of this, the mapping from

+    //      stream time to presentation time is not known.

+    //   -- any presentation-time command due now.

+    // This means that if you want accurate synchronisation on samples that

+    // might be processed during Paused mode, you need to use

+    // stream-time commands.

+    //

+    // In all cases, commands remain queued until Invoked or Cancelled. The

+    // setting and resetting of the event handle is managed entirely by this

+    // queue object.

+

+    // set the clock used for timing

+    virtual HRESULT SetSyncSource(__in_opt IReferenceClock*);

+

+    // switch to run mode. Streamtime to Presentation time mapping known.

+    virtual HRESULT Run(REFERENCE_TIME tStreamTimeOffset);

+

+    // switch to Stopped or Paused mode. Time mapping not known.

+    virtual HRESULT EndRun();

+

+    // return a pointer to the next due command. Blocks for msTimeout

+    // milliseconds until there is a due command.

+    // Stream-time commands will only become due between Run and Endrun calls.

+    // The command remains queued until invoked or cancelled.

+    // Returns E_ABORT if timeout occurs, otherwise S_OK (or other error).

+    // Returns an AddRef-ed object

+    virtual HRESULT GetDueCommand(__out CDeferredCommand ** ppCmd, long msTimeout);

+

+    // return the event handle that will be signalled whenever

+    // there are deferred commands due for execution (when GetDueCommand

+    // will not block).

+    HANDLE GetDueHandle() {

+        return HANDLE(m_evDue);

+    };

+

+    // return a pointer to a command that will be due for a given time.

+    // Pass in a stream time here. The stream time offset will be passed

+    // in via the Run method.

+    // Commands remain queued until invoked or cancelled.

+    // This method will not block. It will report VFW_E_NOT_FOUND if there

+    // are no commands due yet.

+    // Returns an AddRef-ed object

+    virtual HRESULT GetCommandDueFor(REFERENCE_TIME tStream, __out CDeferredCommand**ppCmd);

+

+    // check if a given time is due (TRUE if it is due yet)

+    BOOL CheckTime(CRefTime time, BOOL bStream) {

+

+        // if no clock, nothing is due!

+        if (!m_pClock) {

+            return FALSE;

+        }

+

+        // stream time

+        if (bStream) {

+

+            // not valid if not running

+            if (!m_bRunning) {

+                return FALSE;

+            }

+            // add on known stream time offset to get presentation time

+            time += m_StreamTimeOffset;

+        }

+

+        CRefTime Now;

+        m_pClock->GetTime((REFERENCE_TIME*)&Now);

+        return (time <= Now);

+    };

+

+protected:

+

+    // protect access to lists etc

+    CCritSec m_Lock;

+

+    // commands queued in presentation time are stored here

+    CGenericList<CDeferredCommand> m_listPresentation;

+

+    // commands queued in stream time are stored here

+    CGenericList<CDeferredCommand> m_listStream;

+

+    // set when any commands are due

+    CAMEvent m_evDue;

+

+    // creates an advise for the earliest time required, if any

+    void SetTimeAdvise(void);

+

+    // advise id from reference clock (0 if no outstanding advise)

+    DWORD_PTR m_dwAdvise;

+

+    // advise time is for this presentation time

+    CRefTime m_tCurrentAdvise;

+

+    // the reference clock we are using (addrefed)

+    IReferenceClock* m_pClock;

+

+    // true when running

+    BOOL m_bRunning;

+

+    // contains stream time offset when m_bRunning is true

+    CRefTime m_StreamTimeOffset;

+};

+

+#endif // __CTLUTIL__

diff --git a/jni/pjproject-android/third_party/BaseClasses/fourcc.h b/jni/pjproject-android/third_party/BaseClasses/fourcc.h
new file mode 100644
index 0000000..19c0fcd
--- /dev/null
+++ b/jni/pjproject-android/third_party/BaseClasses/fourcc.h
@@ -0,0 +1,101 @@
+//------------------------------------------------------------------------------

+// File: FourCC.h

+//

+// Desc: DirectShow base classes.

+//

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

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

+

+

+// FOURCCMap

+//

+// provides a mapping between old-style multimedia format DWORDs

+// and new-style GUIDs.

+//

+// A range of 4 billion GUIDs has been allocated to ensure that this

+// mapping can be done straightforwardly one-to-one in both directions.

+//

+// January 95

+

+

+#ifndef __FOURCC__

+#define __FOURCC__

+

+

+// Multimedia format types are marked with DWORDs built from four 8-bit

+// chars and known as FOURCCs. New multimedia AM_MEDIA_TYPE definitions include

+// a subtype GUID. In order to simplify the mapping, GUIDs in the range:

+//    XXXXXXXX-0000-0010-8000-00AA00389B71

+// are reserved for FOURCCs.

+

+class FOURCCMap : public GUID

+{

+

+public:

+    FOURCCMap();

+    FOURCCMap(DWORD Fourcc);

+    FOURCCMap(const GUID *);

+

+

+    DWORD GetFOURCC(void);

+    void SetFOURCC(DWORD fourcc);

+    void SetFOURCC(const GUID *);

+

+private:

+    void InitGUID();

+};

+

+#define GUID_Data2      0

+#define GUID_Data3     0x10

+#define GUID_Data4_1   0xaa000080

+#define GUID_Data4_2   0x719b3800

+

+inline void

+FOURCCMap::InitGUID() {

+    Data2 = GUID_Data2;

+    Data3 = GUID_Data3;

+    ((DWORD *)Data4)[0] = GUID_Data4_1;

+    ((DWORD *)Data4)[1] = GUID_Data4_2;

+}

+

+inline

+FOURCCMap::FOURCCMap() {

+    InitGUID();

+    SetFOURCC( DWORD(0));

+}

+

+inline

+FOURCCMap::FOURCCMap(DWORD fourcc)

+{

+    InitGUID();

+    SetFOURCC(fourcc);

+}

+

+inline

+FOURCCMap::FOURCCMap(const GUID * pGuid)

+{

+    InitGUID();

+    SetFOURCC(pGuid);

+}

+

+inline void

+FOURCCMap::SetFOURCC(const GUID * pGuid)

+{

+    FOURCCMap * p = (FOURCCMap*) pGuid;

+    SetFOURCC(p->GetFOURCC());

+}

+

+inline void

+FOURCCMap::SetFOURCC(DWORD fourcc)

+{

+    Data1 = fourcc;

+}

+

+inline DWORD

+FOURCCMap::GetFOURCC(void)

+{

+    return Data1;

+}

+

+#endif /* __FOURCC__ */

+

diff --git a/jni/pjproject-android/third_party/BaseClasses/measure.h b/jni/pjproject-android/third_party/BaseClasses/measure.h
new file mode 100644
index 0000000..a71a075
--- /dev/null
+++ b/jni/pjproject-android/third_party/BaseClasses/measure.h
@@ -0,0 +1,222 @@
+//------------------------------------------------------------------------------

+// File: Measure.h

+//

+// Desc: DirectShow base classes.

+//

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

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

+

+

+/*

+   The idea is to pepper the source code with interesting measurements and

+   have the last few thousand of these recorded in a circular buffer that

+   can be post-processed to give interesting numbers.

+

+   WHAT THE LOG LOOKS LIKE:

+

+  Time (sec)   Type        Delta  Incident_Name

+    0.055,41  NOTE      -.       Incident Nine  - Another note

+    0.055,42  NOTE      0.000,01 Incident Nine  - Another note

+    0.055,44  NOTE      0.000,02 Incident Nine  - Another note

+    0.055,45  STOP      -.       Incident Eight - Also random

+    0.055,47  START     -.       Incident Seven - Random

+    0.055,49  NOTE      0.000,05 Incident Nine  - Another note

+    ------- <etc.  there is a lot of this> ----------------

+    0.125,60  STOP      0.000,03 Msr_Stop

+    0.125,62  START     -.       Msr_Start

+    0.125,63  START     -.       Incident Two   - Start/Stop

+    0.125,65  STOP      0.000,03 Msr_Start

+    0.125,66  START     -.       Msr_Stop

+    0.125,68  STOP      0.000,05 Incident Two   - Start/Stop

+    0.125,70  STOP      0.000,04 Msr_Stop

+    0.125,72  START     -.       Msr_Start

+    0.125,73  START     -.       Incident Two   - Start/Stop

+    0.125,75  STOP      0.000,03 Msr_Start

+    0.125,77  START     -.       Msr_Stop

+    0.125,78  STOP      0.000,05 Incident Two   - Start/Stop

+    0.125,80  STOP      0.000,03 Msr_Stop

+    0.125,81  NOTE      -.       Incident Three - single Note

+    0.125,83  START     -.       Incident Four  - Start, no stop

+    0.125,85  START     -.       Incident Five  - Single Start/Stop

+    0.125,87  STOP      0.000,02 Incident Five  - Single Start/Stop

+

+Number      Average       StdDev     Smallest      Largest Incident_Name

+    10     0.000,58     0.000,10     0.000,55     0.000,85 Incident One   - Note

+    50     0.000,05     0.000,00     0.000,05     0.000,05 Incident Two   - Start/Stop

+     1     -.           -.           -.           -.       Incident Three - single Note

+     0     -.           -.           -.           -.       Incident Four  - Start, no stop

+     1     0.000,02     -.           0.000,02     0.000,02 Incident Five  - Single Start/Stop

+     0     -.           -.           -.           -.       Incident Six   - zero occurrences

+   100     0.000,25     0.000,12     0.000,02     0.000,62 Incident Seven - Random

+   100     0.000,79     0.000,48     0.000,02     0.001,92 Incident Eight - Also random

+  5895     0.000,01     0.000,01     0.000,01     0.000,56 Incident Nine  - Another note

+    10     0.000,03     0.000,00     0.000,03     0.000,04 Msr_Note

+    50     0.000,03     0.000,00     0.000,03     0.000,04 Msr_Start

+    50     0.000,04     0.000,03     0.000,03     0.000,31 Msr_Stop

+

+  WHAT IT MEANS:

+    The log shows what happened and when.  Each line shows the time at which

+    something happened (see WHAT YOU CODE below) what it was that happened

+    and (if approporate) the time since the corresponding previous event

+    (that's the delta column).

+

+    The statistics show how many times each event occurred, what the average

+    delta time was, also the standard deviation, largest and smalles delta.

+

+   WHAT YOU CODE:

+

+   Before anything else executes: - register your ids

+

+    int id1     = Msr_Register("Incident One   - Note");

+    int id2     = Msr_Register("Incident Two   - Start/Stop");

+    int id3     = Msr_Register("Incident Three - single Note");

+    etc.

+

+   At interesting moments:

+

+       // To measure a repetitive event - e.g. end of bitblt to screen

+       Msr_Note(Id9);             // e.g. "video frame hiting the screen NOW!"

+

+           or

+

+       // To measure an elapsed time e.g. time taken to decode an MPEG B-frame

+       Msr_Start(Id2);            // e.g. "Starting to decode MPEG B-frame"

+         . . .

+       MsrStop(Id2);              //      "Finished MPEG decode"

+

+   At the end:

+

+       HANDLE hFile;

+       hFile = CreateFile("Perf.log", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);

+       Msr_Dump(hFile);           // This writes the log out to the file

+       CloseHandle(hFile);

+

+           or

+

+       Msr_Dump(NULL);            // This writes it to DbgLog((LOG_TRACE,0, ... ));

+                                  // but if you are writing it out to the debugger

+                                  // then the times are probably all garbage because

+                                  // the debugger can make things run awfully slow.

+

+    A given id should be used either for start / stop or Note calls.  If Notes

+    are mixed in with Starts and Stops their statistics will be gibberish.

+

+    If you code the calls in upper case i.e. MSR_START(idMunge); then you get

+    macros which will turn into nothing unless PERF is defined.

+

+    You can reset the statistical counts for a given id by calling Reset(Id).

+    They are reset by default at the start.

+    It logs Reset as a special incident, so you can see it in the log.

+

+    The log is a circular buffer in storage (to try to minimise disk I/O).

+    It overwrites the oldest entries once full.  The statistics include ALL

+    incidents since the last Reset, whether still visible in the log or not.

+*/

+

+#ifndef __MEASURE__

+#define __MEASURE__

+

+#ifdef PERF

+#define MSR_INIT() Msr_Init()

+#define MSR_TERMINATE() Msr_Terminate()

+#define MSR_REGISTER(a) Msr_Register(a)

+#define MSR_RESET(a) Msr_Reset(a)

+#define MSR_CONTROL(a) Msr_Control(a)

+#define MSR_START(a) Msr_Start(a)

+#define MSR_STOP(a) Msr_Stop(a)

+#define MSR_NOTE(a) Msr_Note(a)

+#define MSR_INTEGER(a,b) Msr_Integer(a,b)

+#define MSR_DUMP(a) Msr_Dump(a)

+#define MSR_DUMPSTATS(a) Msr_DumpStats(a)

+#else

+#define MSR_INIT() ((void)0)

+#define MSR_TERMINATE() ((void)0)

+#define MSR_REGISTER(a) 0

+#define MSR_RESET(a) ((void)0)

+#define MSR_CONTROL(a) ((void)0)

+#define MSR_START(a) ((void)0)

+#define MSR_STOP(a) ((void)0)

+#define MSR_NOTE(a) ((void)0)

+#define MSR_INTEGER(a,b) ((void)0)

+#define MSR_DUMP(a) ((void)0)

+#define MSR_DUMPSTATS(a) ((void)0)

+#endif

+

+#ifdef __cplusplus

+extern "C" {

+#endif

+

+// This must be called first - (called by the DllEntry)

+

+void WINAPI Msr_Init(void);

+

+

+// Call this last to clean up (or just let it fall off the end - who cares?)

+

+void WINAPI Msr_Terminate(void);

+

+

+// Call this to get an Id for an "incident" that you can pass to Start, Stop or Note

+// everything that's logged is called an "incident".

+

+int  WINAPI Msr_Register(__in LPTSTR Incident);

+

+

+// Reset the statistical counts for an incident

+

+void WINAPI Msr_Reset(int Id);

+

+

+// Reset all the counts for all incidents

+#define MSR_RESET_ALL 0

+#define MSR_PAUSE 1

+#define MSR_RUN 2

+

+void WINAPI Msr_Control(int iAction);

+

+

+// log the start of an operation

+

+void WINAPI Msr_Start(int Id);

+

+

+// log the end of an operation

+

+void WINAPI Msr_Stop(int Id);

+

+

+// log a one-off or repetitive operation

+

+void WINAPI Msr_Note(int Id);

+

+

+// log an integer (on which we can see statistics later)

+void WINAPI Msr_Integer(int Id, int n);

+

+

+// print out all the vaialable log (it may have wrapped) and then the statistics.

+// When the log wraps you lose log but the statistics are still complete.

+// hFIle==NULL => use DbgLog

+// otherwise hFile must have come from CreateFile or OpenFile.

+

+void WINAPI Msr_Dump(HANDLE hFile);

+

+

+// just dump the statistics - never mind the log

+

+void WINAPI Msr_DumpStats(HANDLE hFile);

+

+// Type definitions in case you want to declare a pointer to the dump functions

+// (makes it a trifle easier to do dynamic linking

+// i.e. LoadModule, GetProcAddress and call that)

+

+// Typedefs so can declare MSR_DUMPPROC *MsrDumpStats; or whatever

+typedef void WINAPI MSR_DUMPPROC(HANDLE hFile);

+typedef void WINAPI MSR_CONTROLPROC(int iAction);

+

+

+#ifdef __cplusplus

+}

+#endif

+

+#endif // __MEASURE__

diff --git a/jni/pjproject-android/third_party/BaseClasses/msgthrd.h b/jni/pjproject-android/third_party/BaseClasses/msgthrd.h
new file mode 100644
index 0000000..45adc01
--- /dev/null
+++ b/jni/pjproject-android/third_party/BaseClasses/msgthrd.h
@@ -0,0 +1,120 @@
+//------------------------------------------------------------------------------

+// File: MsgThrd.h

+//

+// Desc: DirectShow base classes - provides support for a worker thread 

+//       class to which one can asynchronously post messages.

+//

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

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

+

+

+// Message class - really just a structure.

+//

+class CMsg {

+public:

+    UINT uMsg;

+    DWORD dwFlags;

+    LPVOID lpParam;

+    CAMEvent *pEvent;

+

+    CMsg(UINT u, DWORD dw, __inout_opt LPVOID lp, __in_opt CAMEvent *pEvnt)

+        : uMsg(u), dwFlags(dw), lpParam(lp), pEvent(pEvnt) {}

+

+    CMsg()

+        : uMsg(0), dwFlags(0L), lpParam(NULL), pEvent(NULL) {}

+};

+

+// This is the actual thread class.  It exports all the usual thread control

+// functions.  The created thread is different from a normal WIN32 thread in

+// that it is prompted to perform particaular tasks by responding to messages

+// posted to its message queue.

+//

+class AM_NOVTABLE CMsgThread {

+private:

+    static DWORD WINAPI DefaultThreadProc(__inout LPVOID lpParam);

+    DWORD               m_ThreadId;

+    HANDLE              m_hThread;

+

+protected:

+

+    // if you want to override GetThreadMsg to block on other things

+    // as well as this queue, you need access to this

+    CGenericList<CMsg>        m_ThreadQueue;

+    CCritSec                  m_Lock;

+    HANDLE                    m_hSem;

+    LONG                      m_lWaiting;

+

+public:

+    CMsgThread()

+        : m_ThreadId(0),

+        m_hThread(NULL),

+        m_lWaiting(0),

+        m_hSem(NULL),

+        // make a list with a cache of 5 items

+        m_ThreadQueue(NAME("MsgThread list"), 5)

+        {

+        }

+

+    ~CMsgThread();

+    // override this if you want to block on other things as well

+    // as the message loop

+    void virtual GetThreadMsg(__out CMsg *msg);

+

+    // override this if you want to do something on thread startup

+    virtual void OnThreadInit() {

+    };

+

+    BOOL CreateThread();

+

+    BOOL WaitForThreadExit(__out LPDWORD lpdwExitCode) {

+        if (m_hThread != NULL) {

+            WaitForSingleObject(m_hThread, INFINITE);

+            return GetExitCodeThread(m_hThread, lpdwExitCode);

+        }

+        return FALSE;

+    }

+

+    DWORD ResumeThread() {

+        return ::ResumeThread(m_hThread);

+    }

+

+    DWORD SuspendThread() {

+        return ::SuspendThread(m_hThread);

+    }

+

+    int GetThreadPriority() {

+        return ::GetThreadPriority(m_hThread);

+    }

+

+    BOOL SetThreadPriority(int nPriority) {

+        return ::SetThreadPriority(m_hThread, nPriority);

+    }

+

+    HANDLE GetThreadHandle() {

+        return m_hThread;

+    }

+

+    DWORD GetThreadId() {

+        return m_ThreadId;

+    }

+

+

+    void PutThreadMsg(UINT uMsg, DWORD dwMsgFlags,

+                      __in_opt LPVOID lpMsgParam, __in_opt CAMEvent *pEvent = NULL) {

+        CAutoLock lck(&m_Lock);

+        CMsg* pMsg = new CMsg(uMsg, dwMsgFlags, lpMsgParam, pEvent);

+        m_ThreadQueue.AddTail(pMsg);

+        if (m_lWaiting != 0) {

+            ReleaseSemaphore(m_hSem, m_lWaiting, 0);

+            m_lWaiting = 0;

+        }

+    }

+

+    // This is the function prototype of the function that the client

+    // supplies.  It is always called on the created thread, never on

+    // the creator thread.

+    //

+    virtual LRESULT ThreadMessageProc(

+        UINT uMsg, DWORD dwFlags, __inout_opt LPVOID lpParam, __in_opt CAMEvent *pEvent) = 0;

+};

+

diff --git a/jni/pjproject-android/third_party/BaseClasses/mtype.cpp b/jni/pjproject-android/third_party/BaseClasses/mtype.cpp
new file mode 100644
index 0000000..8d99697
--- /dev/null
+++ b/jni/pjproject-android/third_party/BaseClasses/mtype.cpp
@@ -0,0 +1,483 @@
+//------------------------------------------------------------------------------

+// File: MType.cpp

+//

+// Desc: DirectShow base classes - implements a class that holds and 

+//       manages media type information.

+//

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

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

+

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

+

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

+

+// helper class that derived pin objects can use to compare media

+// types etc. Has same data members as the struct AM_MEDIA_TYPE defined

+// in the streams IDL file, but also has (non-virtual) functions

+

+#include <streams.h>

+#include <mmreg.h>

+

+CMediaType::~CMediaType(){

+    FreeMediaType(*this);

+}

+

+

+CMediaType::CMediaType()

+{

+    InitMediaType();

+}

+

+

+CMediaType::CMediaType(const GUID * type)

+{

+    InitMediaType();

+    majortype = *type;

+}

+

+

+// copy constructor does a deep copy of the format block

+

+CMediaType::CMediaType(const AM_MEDIA_TYPE& rt, __out_opt HRESULT* phr)

+{

+    HRESULT hr = CopyMediaType(this, &rt);

+    if (FAILED(hr) && (NULL != phr)) {

+        *phr = hr;

+    }

+}

+

+

+CMediaType::CMediaType(const CMediaType& rt, __out_opt HRESULT* phr)

+{

+    HRESULT hr = CopyMediaType(this, &rt);

+    if (FAILED(hr) && (NULL != phr)) {

+        *phr = hr;

+    }

+}

+

+

+// this class inherits publicly from AM_MEDIA_TYPE so the compiler could generate

+// the following assignment operator itself, however it could introduce some

+// memory conflicts and leaks in the process because the structure contains

+// a dynamically allocated block (pbFormat) which it will not copy correctly

+

+CMediaType&

+CMediaType::operator=(const AM_MEDIA_TYPE& rt)

+{

+    Set(rt);

+    return *this;

+}

+

+CMediaType&

+CMediaType::operator=(const CMediaType& rt)

+{

+    *this = (AM_MEDIA_TYPE &) rt;

+    return *this;

+}

+

+BOOL

+CMediaType::operator == (const CMediaType& rt) const

+{

+    // I don't believe we need to check sample size or

+    // temporal compression flags, since I think these must

+    // be represented in the type, subtype and format somehow. They

+    // are pulled out as separate flags so that people who don't understand

+    // the particular format representation can still see them, but

+    // they should duplicate information in the format block.

+

+    return ((IsEqualGUID(majortype,rt.majortype) == TRUE) &&

+        (IsEqualGUID(subtype,rt.subtype) == TRUE) &&

+        (IsEqualGUID(formattype,rt.formattype) == TRUE) &&

+        (cbFormat == rt.cbFormat) &&

+        ( (cbFormat == 0) ||

+          pbFormat != NULL && rt.pbFormat != NULL &&

+          (memcmp(pbFormat, rt.pbFormat, cbFormat) == 0)));

+}

+

+

+BOOL

+CMediaType::operator != (const CMediaType& rt) const

+{

+    /* Check to see if they are equal */

+

+    if (*this == rt) {

+        return FALSE;

+    }

+    return TRUE;

+}

+

+

+HRESULT

+CMediaType::Set(const CMediaType& rt)

+{

+    return Set((AM_MEDIA_TYPE &) rt);

+}

+

+

+HRESULT

+CMediaType::Set(const AM_MEDIA_TYPE& rt)

+{

+    if (&rt != this) {

+        FreeMediaType(*this);

+        HRESULT hr = CopyMediaType(this, &rt);

+        if (FAILED(hr)) {

+            return E_OUTOFMEMORY;

+        }

+    }

+

+    return S_OK;    

+}

+

+

+BOOL

+CMediaType::IsValid() const

+{

+    return (!IsEqualGUID(majortype,GUID_NULL));

+}

+

+

+void

+CMediaType::SetType(const GUID* ptype)

+{

+    majortype = *ptype;

+}

+

+

+void

+CMediaType::SetSubtype(const GUID* ptype)

+{

+    subtype = *ptype;

+}

+

+

+ULONG

+CMediaType::GetSampleSize() const {

+    if (IsFixedSize()) {

+        return lSampleSize;

+    } else {

+        return 0;

+    }

+}

+

+

+void

+CMediaType::SetSampleSize(ULONG sz) {

+    if (sz == 0) {

+        SetVariableSize();

+    } else {

+        bFixedSizeSamples = TRUE;

+        lSampleSize = sz;

+    }

+}

+

+

+void

+CMediaType::SetVariableSize() {

+    bFixedSizeSamples = FALSE;

+}

+

+

+void

+CMediaType::SetTemporalCompression(BOOL bCompressed) {

+    bTemporalCompression = bCompressed;

+}

+

+BOOL

+CMediaType::SetFormat(__in_bcount(cb) BYTE * pformat, ULONG cb)

+{

+    if (NULL == AllocFormatBuffer(cb))

+	return(FALSE);

+

+    ASSERT(pbFormat);

+    memcpy(pbFormat, pformat, cb);

+    return(TRUE);

+}

+

+

+// set the type of the media type format block, this type defines what you

+// will actually find in the format pointer. For example FORMAT_VideoInfo or

+// FORMAT_WaveFormatEx. In the future this may be an interface pointer to a

+// property set. Before sending out media types this should be filled in.

+

+void

+CMediaType::SetFormatType(const GUID *pformattype)

+{

+    formattype = *pformattype;

+}

+

+

+// reset the format buffer

+

+void CMediaType::ResetFormatBuffer()

+{

+    if (cbFormat) {

+        CoTaskMemFree((PVOID)pbFormat);

+    }

+    cbFormat = 0;

+    pbFormat = NULL;

+}

+

+

+// allocate length bytes for the format and return a read/write pointer

+// If we cannot allocate the new block of memory we return NULL leaving

+// the original block of memory untouched (as does ReallocFormatBuffer)

+

+BYTE*

+CMediaType::AllocFormatBuffer(ULONG length)

+{

+    ASSERT(length);

+

+    // do the types have the same buffer size

+

+    if (cbFormat == length) {

+        return pbFormat;

+    }

+

+    // allocate the new format buffer

+

+    BYTE *pNewFormat = (PBYTE)CoTaskMemAlloc(length);

+    if (pNewFormat == NULL) {

+        if (length <= cbFormat) return pbFormat; //reuse the old block anyway.

+        return NULL;

+    }

+

+    // delete the old format

+

+    if (cbFormat != 0) {

+        ASSERT(pbFormat);

+        CoTaskMemFree((PVOID)pbFormat);

+    }

+

+    cbFormat = length;

+    pbFormat = pNewFormat;

+    return pbFormat;

+}

+

+

+// reallocate length bytes for the format and return a read/write pointer

+// to it. We keep as much information as we can given the new buffer size

+// if this fails the original format buffer is left untouched. The caller

+// is responsible for ensuring the size of memory required is non zero

+

+BYTE*

+CMediaType::ReallocFormatBuffer(ULONG length)

+{

+    ASSERT(length);

+

+    // do the types have the same buffer size

+

+    if (cbFormat == length) {

+        return pbFormat;

+    }

+

+    // allocate the new format buffer

+

+    BYTE *pNewFormat = (PBYTE)CoTaskMemAlloc(length);

+    if (pNewFormat == NULL) {

+        if (length <= cbFormat) return pbFormat; //reuse the old block anyway.

+        return NULL;

+    }

+

+    // copy any previous format (or part of if new is smaller)

+    // delete the old format and replace with the new one

+

+    if (cbFormat != 0) {

+        ASSERT(pbFormat);

+        memcpy(pNewFormat,pbFormat,min(length,cbFormat));

+        CoTaskMemFree((PVOID)pbFormat);

+    }

+

+    cbFormat = length;

+    pbFormat = pNewFormat;

+    return pNewFormat;

+}

+

+// initialise a media type structure

+

+void CMediaType::InitMediaType()

+{

+    ZeroMemory((PVOID)this, sizeof(*this));

+    lSampleSize = 1;

+    bFixedSizeSamples = TRUE;

+}

+

+

+// a partially specified media type can be passed to IPin::Connect

+// as a constraint on the media type used in the connection.

+// the type, subtype or format type can be null.

+BOOL

+CMediaType::IsPartiallySpecified(void) const

+{

+    if ((majortype == GUID_NULL) ||

+        (formattype == GUID_NULL)) {

+            return TRUE;

+    } else {

+        return FALSE;

+    }

+}

+

+BOOL

+CMediaType::MatchesPartial(const CMediaType* ppartial) const

+{

+    if ((ppartial->majortype != GUID_NULL) &&

+        (majortype != ppartial->majortype)) {

+            return FALSE;

+    }

+    if ((ppartial->subtype != GUID_NULL) &&

+        (subtype != ppartial->subtype)) {

+            return FALSE;

+    }

+

+    if (ppartial->formattype != GUID_NULL) {

+        // if the format block is specified then it must match exactly

+        if (formattype != ppartial->formattype) {

+            return FALSE;

+        }

+        if (cbFormat != ppartial->cbFormat) {

+            return FALSE;

+        }

+        if ((cbFormat != 0) &&

+            (memcmp(pbFormat, ppartial->pbFormat, cbFormat) != 0)) {

+                return FALSE;

+        }

+    }

+

+    return TRUE;

+

+}

+

+

+

+// general purpose function to delete a heap allocated AM_MEDIA_TYPE structure

+// which is useful when calling IEnumMediaTypes::Next as the interface

+// implementation allocates the structures which you must later delete

+// the format block may also be a pointer to an interface to release

+

+void WINAPI DeleteMediaType(__inout_opt AM_MEDIA_TYPE *pmt)

+{

+    // allow NULL pointers for coding simplicity

+

+    if (pmt == NULL) {

+        return;

+    }

+

+    FreeMediaType(*pmt);

+    CoTaskMemFree((PVOID)pmt);

+}

+

+

+// this also comes in useful when using the IEnumMediaTypes interface so

+// that you can copy a media type, you can do nearly the same by creating

+// a CMediaType object but as soon as it goes out of scope the destructor

+// will delete the memory it allocated (this takes a copy of the memory)

+

+AM_MEDIA_TYPE * WINAPI CreateMediaType(AM_MEDIA_TYPE const *pSrc)

+{

+    ASSERT(pSrc);

+

+    // Allocate a block of memory for the media type

+

+    AM_MEDIA_TYPE *pMediaType =

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

+

+    if (pMediaType == NULL) {

+        return NULL;

+    }

+    // Copy the variable length format block

+

+    HRESULT hr = CopyMediaType(pMediaType,pSrc);

+    if (FAILED(hr)) {

+        CoTaskMemFree((PVOID)pMediaType);

+        return NULL;

+    }

+

+    return pMediaType;

+}

+

+

+//  Copy 1 media type to another

+

+HRESULT WINAPI CopyMediaType(__out AM_MEDIA_TYPE *pmtTarget, const AM_MEDIA_TYPE *pmtSource)

+{

+    //  We'll leak if we copy onto one that already exists - there's one

+    //  case we can check like that - copying to itself.

+    ASSERT(pmtSource != pmtTarget);

+    *pmtTarget = *pmtSource;

+    if (pmtSource->cbFormat != 0) {

+        ASSERT(pmtSource->pbFormat != NULL);

+        pmtTarget->pbFormat = (PBYTE)CoTaskMemAlloc(pmtSource->cbFormat);

+        if (pmtTarget->pbFormat == NULL) {

+            pmtTarget->cbFormat = 0;

+            return E_OUTOFMEMORY;

+        } else {

+            CopyMemory((PVOID)pmtTarget->pbFormat, (PVOID)pmtSource->pbFormat,

+                       pmtTarget->cbFormat);

+        }

+    }

+    if (pmtTarget->pUnk != NULL) {

+        pmtTarget->pUnk->AddRef();

+    }

+

+    return S_OK;

+}

+

+//  Free an existing media type (ie free resources it holds)

+

+void WINAPI FreeMediaType(__inout AM_MEDIA_TYPE& mt)

+{

+    if (mt.cbFormat != 0) {

+        CoTaskMemFree((PVOID)mt.pbFormat);

+

+        // Strictly unnecessary but tidier

+        mt.cbFormat = 0;

+        mt.pbFormat = NULL;

+    }

+    if (mt.pUnk != NULL) {

+        mt.pUnk->Release();

+        mt.pUnk = NULL;

+    }

+}

+

+//  Initialize a media type from a WAVEFORMATEX

+

+STDAPI CreateAudioMediaType(

+    const WAVEFORMATEX *pwfx,

+    __out AM_MEDIA_TYPE *pmt,

+    BOOL bSetFormat

+)

+{

+    pmt->majortype            = MEDIATYPE_Audio;

+    if (pwfx->wFormatTag == WAVE_FORMAT_EXTENSIBLE) {

+        pmt->subtype = ((PWAVEFORMATEXTENSIBLE)pwfx)->SubFormat;

+    } else {

+        pmt->subtype              = FOURCCMap(pwfx->wFormatTag);

+    }

+    pmt->formattype           = FORMAT_WaveFormatEx;

+    pmt->bFixedSizeSamples    = TRUE;

+    pmt->bTemporalCompression = FALSE;

+    pmt->lSampleSize          = pwfx->nBlockAlign;

+    pmt->pUnk                 = NULL;

+    if (bSetFormat) {

+        if (pwfx->wFormatTag == WAVE_FORMAT_PCM) {

+            pmt->cbFormat         = sizeof(WAVEFORMATEX);

+        } else {

+            pmt->cbFormat         = sizeof(WAVEFORMATEX) + pwfx->cbSize;

+        }

+        pmt->pbFormat             = (PBYTE)CoTaskMemAlloc(pmt->cbFormat);

+        if (pmt->pbFormat == NULL) {

+            return E_OUTOFMEMORY;

+        }

+        if (pwfx->wFormatTag == WAVE_FORMAT_PCM) {

+            CopyMemory(pmt->pbFormat, pwfx, sizeof(PCMWAVEFORMAT));

+            ((WAVEFORMATEX *)pmt->pbFormat)->cbSize = 0;

+        } else {

+            CopyMemory(pmt->pbFormat, pwfx, pmt->cbFormat);

+        }

+    }

+    return S_OK;

+}

+

+// eliminate very many spurious warnings from MS compiler

+#pragma warning(disable:4514)

+

+#endif /* PJMEDIA_VIDEO_DEV_HAS_DSHOW */

diff --git a/jni/pjproject-android/third_party/BaseClasses/mtype.h b/jni/pjproject-android/third_party/BaseClasses/mtype.h
new file mode 100644
index 0000000..fc2fe53
--- /dev/null
+++ b/jni/pjproject-android/third_party/BaseClasses/mtype.h
@@ -0,0 +1,89 @@
+//------------------------------------------------------------------------------

+// File: MtType.h

+//

+// Desc: DirectShow base classes - defines a class that holds and manages

+//       media type information.

+//

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

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

+

+

+#ifndef __MTYPE__

+#define __MTYPE__

+

+/* Helper class that derived pin objects can use to compare media

+   types etc. Has same data members as the struct AM_MEDIA_TYPE defined

+   in the streams IDL file, but also has (non-virtual) functions */

+

+class CMediaType : public _AMMediaType {

+

+public:

+

+    ~CMediaType();

+    CMediaType();

+    CMediaType(const GUID * majortype);

+    CMediaType(const AM_MEDIA_TYPE&, __out_opt HRESULT* phr = NULL);

+    CMediaType(const CMediaType&, __out_opt HRESULT* phr = NULL);

+

+    CMediaType& operator=(const CMediaType&);

+    CMediaType& operator=(const AM_MEDIA_TYPE&);

+

+    BOOL operator == (const CMediaType&) const;

+    BOOL operator != (const CMediaType&) const;

+

+    HRESULT Set(const CMediaType& rt);

+    HRESULT Set(const AM_MEDIA_TYPE& rt);

+

+    BOOL IsValid() const;

+

+    const GUID *Type() const { return &majortype;} ;

+    void SetType(const GUID *);

+    const GUID *Subtype() const { return &subtype;} ;

+    void SetSubtype(const GUID *);

+

+    BOOL IsFixedSize() const {return bFixedSizeSamples; };

+    BOOL IsTemporalCompressed() const {return bTemporalCompression; };

+    ULONG GetSampleSize() const;

+

+    void SetSampleSize(ULONG sz);

+    void SetVariableSize();

+    void SetTemporalCompression(BOOL bCompressed);

+

+    // read/write pointer to format - can't change length without

+    // calling SetFormat, AllocFormatBuffer or ReallocFormatBuffer

+

+    BYTE*   Format() const {return pbFormat; };

+    ULONG   FormatLength() const { return cbFormat; };

+

+    void SetFormatType(const GUID *);

+    const GUID *FormatType() const {return &formattype; };

+    BOOL SetFormat(__in_bcount(length) BYTE *pFormat, ULONG length);

+    void ResetFormatBuffer();

+    BYTE* AllocFormatBuffer(ULONG length);

+    BYTE* ReallocFormatBuffer(ULONG length);

+

+    void InitMediaType();

+

+    BOOL MatchesPartial(const CMediaType* ppartial) const;

+    BOOL IsPartiallySpecified(void) const;

+};

+

+

+/* General purpose functions to copy and delete a task allocated AM_MEDIA_TYPE

+   structure which is useful when using the IEnumMediaFormats interface as

+   the implementation allocates the structures which you must later delete */

+

+void WINAPI DeleteMediaType(__inout_opt AM_MEDIA_TYPE *pmt);

+AM_MEDIA_TYPE * WINAPI CreateMediaType(AM_MEDIA_TYPE const *pSrc);

+HRESULT WINAPI CopyMediaType(__out AM_MEDIA_TYPE *pmtTarget, const AM_MEDIA_TYPE *pmtSource);

+void WINAPI FreeMediaType(__inout AM_MEDIA_TYPE& mt);

+

+//  Initialize a media type from a WAVEFORMATEX

+

+STDAPI CreateAudioMediaType(

+    const WAVEFORMATEX *pwfx,

+    __out AM_MEDIA_TYPE *pmt,

+    BOOL bSetFormat);

+

+#endif /* __MTYPE__ */

+

diff --git a/jni/pjproject-android/third_party/BaseClasses/refclock.h b/jni/pjproject-android/third_party/BaseClasses/refclock.h
new file mode 100644
index 0000000..d2b0bb1
--- /dev/null
+++ b/jni/pjproject-android/third_party/BaseClasses/refclock.h
@@ -0,0 +1,184 @@
+//------------------------------------------------------------------------------

+// File: RefClock.h

+//

+// Desc: DirectShow base classes - defines the IReferenceClock interface.

+//

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

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

+

+

+#ifndef __BASEREFCLOCK__

+#define __BASEREFCLOCK__

+

+#include <Schedule.h>

+

+const UINT RESOLUTION = 1;                      /* High resolution timer */

+const INT ADVISE_CACHE = 4;                     /* Default cache size */

+const LONGLONG MAX_TIME = 0x7FFFFFFFFFFFFFFF;   /* Maximum LONGLONG value */

+

+inline LONGLONG WINAPI ConvertToMilliseconds(const REFERENCE_TIME& RT)

+{

+    /* This converts an arbitrary value representing a reference time

+       into a MILLISECONDS value for use in subsequent system calls */

+

+    return (RT / (UNITS / MILLISECONDS));

+}

+

+/* This class hierarchy will support an IReferenceClock interface so

+   that an audio card (or other externally driven clock) can update the

+   system wide clock that everyone uses.

+

+   The interface will be pretty thin with probably just one update method

+   This interface has not yet been defined.

+ */

+

+/* This abstract base class implements the IReferenceClock

+ * interface.  Classes that actually provide clock signals (from

+ * whatever source) have to be derived from this class.

+ *

+ * The abstract class provides implementations for:

+ *  CUnknown support

+ *      locking support (CCritSec)

+ *  client advise code (creates a thread)

+ *

+ * Question: what can we do about quality?  Change the timer

+ * resolution to lower the system load?  Up the priority of the

+ * timer thread to force more responsive signals?

+ *

+ * During class construction we create a worker thread that is destroyed during

+ * destuction.  This thread executes a series of WaitForSingleObject calls,

+ * waking up when a command is given to the thread or the next wake up point

+ * is reached.  The wakeup points are determined by clients making Advise

+ * calls.

+ *

+ * Each advise call defines a point in time when they wish to be notified.  A

+ * periodic advise is a series of these such events.  We maintain a list of

+ * advise links and calculate when the nearest event notification is due for.

+ * We then call WaitForSingleObject with a timeout equal to this time.  The

+ * handle we wait on is used by the class to signal that something has changed

+ * and that we must reschedule the next event.  This typically happens when

+ * someone comes in and asks for an advise link while we are waiting for an

+ * event to timeout.

+ *

+ * While we are modifying the list of advise requests we

+ * are protected from interference through a critical section.  Clients are NOT

+ * advised through callbacks.  One shot clients have an event set, while

+ * periodic clients have a semaphore released for each event notification.  A

+ * semaphore allows a client to be kept up to date with the number of events

+ * actually triggered and be assured that they can't miss multiple events being

+ * set.

+ *

+ * Keeping track of advises is taken care of by the CAMSchedule class.

+ */

+

+class CBaseReferenceClock

+: public CUnknown, public IReferenceClock, public CCritSec, public IReferenceClockTimerControl 

+{

+protected:

+    virtual ~CBaseReferenceClock();     // Don't let me be created on the stack!

+public:

+    CBaseReferenceClock(__in_opt LPCTSTR pName, 

+                        __inout_opt LPUNKNOWN pUnk, 

+                        __inout HRESULT *phr, 

+                        __inout_opt CAMSchedule * pSched = 0 );

+

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

+

+    DECLARE_IUNKNOWN

+

+    /* IReferenceClock methods */

+    // Derived classes must implement GetPrivateTime().  All our GetTime

+    // does is call GetPrivateTime and then check so that time does not

+    // go backwards.  A return code of S_FALSE implies that the internal

+    // clock has gone backwards and GetTime time has halted until internal

+    // time has caught up. (Don't know if this will be much use to folk,

+    // but it seems odd not to use the return code for something useful.)

+    STDMETHODIMP GetTime(__out REFERENCE_TIME *pTime);

+    // When this is called, it sets m_rtLastGotTime to the time it returns.

+

+    /* Provide standard mechanisms for scheduling events */

+

+    /* Ask for an async notification that a time has elapsed */

+    STDMETHODIMP AdviseTime(

+        REFERENCE_TIME baseTime,        // base reference time

+        REFERENCE_TIME streamTime,      // stream offset time

+        HEVENT hEvent,                  // advise via this event

+        __out DWORD_PTR *pdwAdviseCookie// where your cookie goes

+    );

+

+    /* Ask for an asynchronous periodic notification that a time has elapsed */

+    STDMETHODIMP AdvisePeriodic(

+        REFERENCE_TIME StartTime,       // starting at this time

+        REFERENCE_TIME PeriodTime,      // time between notifications

+        HSEMAPHORE hSemaphore,          // advise via a semaphore

+        __out DWORD_PTR *pdwAdviseCookie// where your cookie goes

+    );

+

+    /* Cancel a request for notification(s) - if the notification was

+     * a one shot timer then this function doesn't need to be called

+     * as the advise is automatically cancelled, however it does no

+     * harm to explicitly cancel a one-shot advise.  It is REQUIRED that

+     * clients call Unadvise to clear a Periodic advise setting.

+     */

+

+    STDMETHODIMP Unadvise(DWORD_PTR dwAdviseCookie);

+

+    /* Methods for the benefit of derived classes or outer objects */

+

+    // GetPrivateTime() is the REAL clock.  GetTime is just a cover for

+    // it.  Derived classes will probably override this method but not

+    // GetTime() itself.

+    // The important point about GetPrivateTime() is it's allowed to go

+    // backwards.  Our GetTime() will keep returning the LastGotTime

+    // until GetPrivateTime() catches up.

+    virtual REFERENCE_TIME GetPrivateTime();

+

+    /* Provide a method for correcting drift */

+    STDMETHODIMP SetTimeDelta( const REFERENCE_TIME& TimeDelta );

+

+    CAMSchedule * GetSchedule() const { return m_pSchedule; }

+

+    // IReferenceClockTimerControl methods

+    //

+    // Setting a default of 0 disables the default of 1ms

+    STDMETHODIMP SetDefaultTimerResolution(

+        REFERENCE_TIME timerResolution // in 100ns

+    );

+    STDMETHODIMP GetDefaultTimerResolution(

+        __out REFERENCE_TIME* pTimerResolution // in 100ns

+    );

+

+private:

+    REFERENCE_TIME m_rtPrivateTime;     // Current best estimate of time

+    DWORD          m_dwPrevSystemTime;  // Last vaule we got from timeGetTime

+    REFERENCE_TIME m_rtLastGotTime;     // Last time returned by GetTime

+    REFERENCE_TIME m_rtNextAdvise;      // Time of next advise

+    UINT           m_TimerResolution;

+

+#ifdef PERF

+    int m_idGetSystemTime;

+#endif

+

+// Thread stuff

+public:

+    void TriggerThread()    // Wakes thread up.  Need to do this if

+    {                       // time to next advise needs reevaluating.

+        EXECUTE_ASSERT(SetEvent(m_pSchedule->GetEvent()));

+    }

+

+

+private:

+    BOOL           m_bAbort;            // Flag used for thread shutdown

+    HANDLE         m_hThread;           // Thread handle

+

+    HRESULT AdviseThread();             // Method in which the advise thread runs

+    static DWORD __stdcall AdviseThreadFunction(__in LPVOID); // Function used to get there

+

+protected:

+    CAMSchedule * m_pSchedule;

+

+    void Restart (IN REFERENCE_TIME rtMinTime = 0I64) ;

+};

+

+#endif

+

diff --git a/jni/pjproject-android/third_party/BaseClasses/reftime.h b/jni/pjproject-android/third_party/BaseClasses/reftime.h
new file mode 100644
index 0000000..5bc99a6
--- /dev/null
+++ b/jni/pjproject-android/third_party/BaseClasses/reftime.h
@@ -0,0 +1,116 @@
+//------------------------------------------------------------------------------

+// File: RefTime.h

+//

+// Desc: DirectShow base classes - defines CRefTime, a class that manages

+//       reference times.

+//

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

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

+

+

+//

+// CRefTime

+//

+// Manage reference times.

+// Shares same data layout as REFERENCE_TIME, but adds some (nonvirtual)

+// functions providing simple comparison, conversion and arithmetic.

+//

+// A reference time (at the moment) is a unit of seconds represented in

+// 100ns units as is used in the Win32 FILETIME structure. BUT the time

+// a REFERENCE_TIME represents is NOT the time elapsed since 1/1/1601 it

+// will either be stream time or reference time depending upon context

+//

+// This class provides simple arithmetic operations on reference times

+//

+// keep non-virtual otherwise the data layout will not be the same as

+// REFERENCE_TIME

+

+

+// -----

+// note that you are safe to cast a CRefTime* to a REFERENCE_TIME*, but

+// you will need to do so explicitly

+// -----

+

+

+#ifndef __REFTIME__

+#define __REFTIME__

+

+

+const LONGLONG MILLISECONDS = (1000);            // 10 ^ 3

+const LONGLONG NANOSECONDS = (1000000000);       // 10 ^ 9

+const LONGLONG UNITS = (NANOSECONDS / 100);      // 10 ^ 7

+

+/*  Unfortunately an inline function here generates a call to __allmul

+    - even for constants!

+*/

+#define MILLISECONDS_TO_100NS_UNITS(lMs) \

+    Int32x32To64((lMs), (UNITS / MILLISECONDS))

+

+class CRefTime

+{

+public:

+

+    // *MUST* be the only data member so that this class is exactly

+    // equivalent to a REFERENCE_TIME.

+    // Also, must be *no virtual functions*

+

+    REFERENCE_TIME m_time;

+

+    inline CRefTime()

+    {

+        // default to 0 time

+        m_time = 0;

+    };

+

+    inline CRefTime(LONG msecs)

+    {

+        m_time = MILLISECONDS_TO_100NS_UNITS(msecs);

+    };

+

+    inline CRefTime(REFERENCE_TIME rt)

+    {

+        m_time = rt;

+    };

+

+    inline operator REFERENCE_TIME() const

+    {

+        return m_time;

+    };

+

+    inline CRefTime& operator=(const CRefTime& rt)

+    {

+        m_time = rt.m_time;

+        return *this;

+    };

+

+    inline CRefTime& operator=(const LONGLONG ll)

+    {

+        m_time = ll;

+        return *this;

+    };

+

+    inline CRefTime& operator+=(const CRefTime& rt)

+    {

+        return (*this = *this + rt);

+    };

+

+    inline CRefTime& operator-=(const CRefTime& rt)

+    {

+        return (*this = *this - rt);

+    };

+

+    inline LONG Millisecs(void)

+    {

+        return (LONG)(m_time / (UNITS / MILLISECONDS));

+    };

+

+    inline LONGLONG GetUnits(void)

+    {

+        return m_time;

+    };

+};

+

+const LONGLONG TimeZero = 0;

+

+#endif /* __REFTIME__ */

+

diff --git a/jni/pjproject-android/third_party/BaseClasses/renbase.cpp b/jni/pjproject-android/third_party/BaseClasses/renbase.cpp
new file mode 100644
index 0000000..b354b5f
--- /dev/null
+++ b/jni/pjproject-android/third_party/BaseClasses/renbase.cpp
@@ -0,0 +1,2862 @@
+//------------------------------------------------------------------------------

+// File: RenBase.cpp

+//

+// Desc: DirectShow base classes.

+//

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

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

+

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

+

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

+

+#include <streams.h>        // DirectShow base class definitions

+#include <mmsystem.h>       // Needed for definition of timeGetTime

+#include <limits.h>         // Standard data type limit definitions

+#include <measure.h>        // Used for time critical log functions

+

+#pragma warning(disable:4355)

+

+//  Helper function for clamping time differences

+int inline TimeDiff(REFERENCE_TIME rt)

+{

+    if (rt < - (50 * UNITS)) {

+        return -(50 * UNITS);

+    } else

+    if (rt > 50 * UNITS) {

+        return 50 * UNITS;

+    } else return (int)rt;

+}

+

+// Implements the CBaseRenderer class

+

+CBaseRenderer::CBaseRenderer(REFCLSID RenderClass, // CLSID for this renderer

+                             __in_opt LPCTSTR pName,         // Debug ONLY description

+                             __inout_opt LPUNKNOWN pUnk,       // Aggregated owner object

+                             __inout HRESULT *phr) :       // General OLE return code

+

+    CBaseFilter(pName,pUnk,&m_InterfaceLock,RenderClass),

+    m_evComplete(TRUE, phr),

+    m_RenderEvent(FALSE, phr),

+    m_bAbort(FALSE),

+    m_pPosition(NULL),

+    m_ThreadSignal(TRUE, phr),

+    m_bStreaming(FALSE),

+    m_bEOS(FALSE),

+    m_bEOSDelivered(FALSE),

+    m_pMediaSample(NULL),

+    m_dwAdvise(0),

+    m_pQSink(NULL),

+    m_pInputPin(NULL),

+    m_bRepaintStatus(TRUE),

+    m_SignalTime(0),

+    m_bInReceive(FALSE),

+    m_EndOfStreamTimer(0)

+{

+    if (SUCCEEDED(*phr)) {

+        Ready();

+#ifdef PERF

+        m_idBaseStamp = MSR_REGISTER(TEXT("BaseRenderer: sample time stamp"));

+        m_idBaseRenderTime = MSR_REGISTER(TEXT("BaseRenderer: draw time (msec)"));

+        m_idBaseAccuracy = MSR_REGISTER(TEXT("BaseRenderer: Accuracy (msec)"));

+#endif

+    }

+}

+

+

+// Delete the dynamically allocated IMediaPosition and IMediaSeeking helper

+// object. The object is created when somebody queries us. These are standard

+// control interfaces for seeking and setting start/stop positions and rates.

+// We will probably also have made an input pin based on CRendererInputPin

+// that has to be deleted, it's created when an enumerator calls our GetPin

+

+CBaseRenderer::~CBaseRenderer()

+{

+    ASSERT(m_bStreaming == FALSE);

+    ASSERT(m_EndOfStreamTimer == 0);

+    StopStreaming();

+    ClearPendingSample();

+

+    // Delete any IMediaPosition implementation

+

+    if (m_pPosition) {

+        delete m_pPosition;

+        m_pPosition = NULL;

+    }

+

+    // Delete any input pin created

+

+    if (m_pInputPin) {

+        delete m_pInputPin;

+        m_pInputPin = NULL;

+    }

+

+    // Release any Quality sink

+

+    ASSERT(m_pQSink == NULL);

+}

+

+

+// This returns the IMediaPosition and IMediaSeeking interfaces

+

+HRESULT CBaseRenderer::GetMediaPositionInterface(REFIID riid, __deref_out void **ppv)

+{

+    CAutoLock cObjectCreationLock(&m_ObjectCreationLock);

+    if (m_pPosition) {

+        return m_pPosition->NonDelegatingQueryInterface(riid,ppv);

+    }

+

+    CBasePin *pPin = GetPin(0);

+    if (NULL == pPin) {

+        return E_OUTOFMEMORY;

+    }

+

+    HRESULT hr = NOERROR;

+

+    // Create implementation of this dynamically since sometimes we may

+    // never try and do a seek. The helper object implements a position

+    // control interface (IMediaPosition) which in fact simply takes the

+    // calls normally from the filter graph and passes them upstream

+

+    m_pPosition = new CRendererPosPassThru(NAME("Renderer CPosPassThru"),

+                                           CBaseFilter::GetOwner(),

+                                           (HRESULT *) &hr,

+                                           pPin);

+    if (m_pPosition == NULL) {

+        return E_OUTOFMEMORY;

+    }

+

+    if (FAILED(hr)) {

+        delete m_pPosition;

+        m_pPosition = NULL;

+        return E_NOINTERFACE;

+    }

+    return GetMediaPositionInterface(riid,ppv);

+}

+

+

+// Overriden to say what interfaces we support and where

+

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

+{

+    // Do we have this interface

+

+    if (riid == IID_IMediaPosition || riid == IID_IMediaSeeking) {

+        return GetMediaPositionInterface(riid,ppv);

+    } else {

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

+    }

+}

+

+

+// This is called whenever we change states, we have a manual reset event that

+// is signalled whenever we don't won't the source filter thread to wait in us

+// (such as in a stopped state) and likewise is not signalled whenever it can

+// wait (during paused and running) this function sets or resets the thread

+// event. The event is used to stop source filter threads waiting in Receive

+

+HRESULT CBaseRenderer::SourceThreadCanWait(BOOL bCanWait)

+{

+    if (bCanWait == TRUE) {

+        m_ThreadSignal.Reset();

+    } else {

+        m_ThreadSignal.Set();

+    }

+    return NOERROR;

+}

+

+

+#ifdef DEBUG

+// Dump the current renderer state to the debug terminal. The hardest part of

+// the renderer is the window where we unlock everything to wait for a clock

+// to signal it is time to draw or for the application to cancel everything

+// by stopping the filter. If we get things wrong we can leave the thread in

+// WaitForRenderTime with no way for it to ever get out and we will deadlock

+

+void CBaseRenderer::DisplayRendererState()

+{

+    DbgLog((LOG_TIMING, 1, TEXT("\nTimed out in WaitForRenderTime")));

+

+    // No way should this be signalled at this point

+

+    BOOL bSignalled = m_ThreadSignal.Check();

+    DbgLog((LOG_TIMING, 1, TEXT("Signal sanity check %d"),bSignalled));

+

+    // Now output the current renderer state variables

+

+    DbgLog((LOG_TIMING, 1, TEXT("Filter state %d"),m_State));

+

+    DbgLog((LOG_TIMING, 1, TEXT("Abort flag %d"),m_bAbort));

+

+    DbgLog((LOG_TIMING, 1, TEXT("Streaming flag %d"),m_bStreaming));

+

+    DbgLog((LOG_TIMING, 1, TEXT("Clock advise link %d"),m_dwAdvise));

+

+    DbgLog((LOG_TIMING, 1, TEXT("Current media sample %x"),m_pMediaSample));

+

+    DbgLog((LOG_TIMING, 1, TEXT("EOS signalled %d"),m_bEOS));

+

+    DbgLog((LOG_TIMING, 1, TEXT("EOS delivered %d"),m_bEOSDelivered));

+

+    DbgLog((LOG_TIMING, 1, TEXT("Repaint status %d"),m_bRepaintStatus));

+

+

+    // Output the delayed end of stream timer information

+

+    DbgLog((LOG_TIMING, 1, TEXT("End of stream timer %x"),m_EndOfStreamTimer));

+

+    DbgLog((LOG_TIMING, 1, TEXT("Deliver time %s"),CDisp((LONGLONG)m_SignalTime)));

+

+

+    // Should never timeout during a flushing state

+

+    BOOL bFlushing = m_pInputPin->IsFlushing();

+    DbgLog((LOG_TIMING, 1, TEXT("Flushing sanity check %d"),bFlushing));

+

+    // Display the time we were told to start at

+    DbgLog((LOG_TIMING, 1, TEXT("Last run time %s"),CDisp((LONGLONG)m_tStart.m_time)));

+

+    // Have we got a reference clock

+    if (m_pClock == NULL) return;

+

+    // Get the current time from the wall clock

+

+    CRefTime CurrentTime,StartTime,EndTime;

+    m_pClock->GetTime((REFERENCE_TIME*) &CurrentTime);

+    CRefTime Offset = CurrentTime - m_tStart;

+

+    // Display the current time from the clock

+

+    DbgLog((LOG_TIMING, 1, TEXT("Clock time %s"),CDisp((LONGLONG)CurrentTime.m_time)));

+

+    DbgLog((LOG_TIMING, 1, TEXT("Time difference %dms"),Offset.Millisecs()));

+

+

+    // Do we have a sample ready to render

+    if (m_pMediaSample == NULL) return;

+

+    m_pMediaSample->GetTime((REFERENCE_TIME*)&StartTime, (REFERENCE_TIME*)&EndTime);

+    DbgLog((LOG_TIMING, 1, TEXT("Next sample stream times (Start %d End %d ms)"),

+           StartTime.Millisecs(),EndTime.Millisecs()));

+

+    // Calculate how long it is until it is due for rendering

+    CRefTime Wait = (m_tStart + StartTime) - CurrentTime;

+    DbgLog((LOG_TIMING, 1, TEXT("Wait required %d ms"),Wait.Millisecs()));

+}

+#endif

+

+

+// Wait until the clock sets the timer event or we're otherwise signalled. We

+// set an arbitrary timeout for this wait and if it fires then we display the

+// current renderer state on the debugger. It will often fire if the filter's

+// left paused in an application however it may also fire during stress tests

+// if the synchronisation with application seeks and state changes is faulty

+

+#define RENDER_TIMEOUT 10000

+

+HRESULT CBaseRenderer::WaitForRenderTime()

+{

+    HANDLE WaitObjects[] = { m_ThreadSignal, m_RenderEvent };

+    DWORD Result = WAIT_TIMEOUT;

+

+    // Wait for either the time to arrive or for us to be stopped

+

+    OnWaitStart();

+    while (Result == WAIT_TIMEOUT) {

+        Result = WaitForMultipleObjects(2,WaitObjects,FALSE,RENDER_TIMEOUT);

+

+#ifdef DEBUG

+        if (Result == WAIT_TIMEOUT) DisplayRendererState();

+#endif

+

+    }

+    OnWaitEnd();

+

+    // We may have been awoken without the timer firing

+

+    if (Result == WAIT_OBJECT_0) {

+        return VFW_E_STATE_CHANGED;

+    }

+

+    SignalTimerFired();

+    return NOERROR;

+}

+

+

+// Poll waiting for Receive to complete.  This really matters when

+// Receive may set the palette and cause window messages

+// The problem is that if we don't really wait for a renderer to

+// stop processing we can deadlock waiting for a transform which

+// is calling the renderer's Receive() method because the transform's

+// Stop method doesn't know to process window messages to unblock

+// the renderer's Receive processing

+void CBaseRenderer::WaitForReceiveToComplete()

+{

+    for (;;) {

+        if (!m_bInReceive) {

+            break;

+        }

+

+        MSG msg;

+        //  Receive all interthread snedmessages

+        PeekMessage(&msg, NULL, WM_NULL, WM_NULL, PM_NOREMOVE);

+

+        Sleep(1);

+    }

+

+    // If the wakebit for QS_POSTMESSAGE is set, the PeekMessage call

+    // above just cleared the changebit which will cause some messaging

+    // calls to block (waitMessage, MsgWaitFor...) now.

+    // Post a dummy message to set the QS_POSTMESSAGE bit again

+    if (HIWORD(GetQueueStatus(QS_POSTMESSAGE)) & QS_POSTMESSAGE) {

+        //  Send dummy message

+        PostThreadMessage(GetCurrentThreadId(), WM_NULL, 0, 0);

+    }

+}

+

+// A filter can have four discrete states, namely Stopped, Running, Paused,

+// Intermediate. We are in an intermediate state if we are currently trying

+// to pause but haven't yet got the first sample (or if we have been flushed

+// in paused state and therefore still have to wait for a sample to arrive)

+

+// This class contains an event called m_evComplete which is signalled when

+// the current state is completed and is not signalled when we are waiting to

+// complete the last state transition. As mentioned above the only time we

+// use this at the moment is when we wait for a media sample in paused state

+// If while we are waiting we receive an end of stream notification from the

+// source filter then we know no data is imminent so we can reset the event

+// This means that when we transition to paused the source filter must call

+// end of stream on us or send us an image otherwise we'll hang indefinately

+

+

+// Simple internal way of getting the real state

+

+FILTER_STATE CBaseRenderer::GetRealState() {

+    return m_State;

+}

+

+

+// The renderer doesn't complete the full transition to paused states until

+// it has got one media sample to render. If you ask it for its state while

+// it's waiting it will return the state along with VFW_S_STATE_INTERMEDIATE

+

+STDMETHODIMP CBaseRenderer::GetState(DWORD dwMSecs,FILTER_STATE *State)

+{

+    CheckPointer(State,E_POINTER);

+

+    if (WaitDispatchingMessages(m_evComplete, dwMSecs) == WAIT_TIMEOUT) {

+        *State = m_State;

+        return VFW_S_STATE_INTERMEDIATE;

+    }

+    *State = m_State;

+    return NOERROR;

+}

+

+

+// If we're pausing and we have no samples we don't complete the transition

+// to State_Paused and we return S_FALSE. However if the m_bAbort flag has

+// been set then all samples are rejected so there is no point waiting for

+// one. If we do have a sample then return NOERROR. We will only ever return

+// VFW_S_STATE_INTERMEDIATE from GetState after being paused with no sample

+// (calling GetState after either being stopped or Run will NOT return this)

+

+HRESULT CBaseRenderer::CompleteStateChange(FILTER_STATE OldState)

+{

+    // Allow us to be paused when disconnected

+

+    if (m_pInputPin->IsConnected() == FALSE) {

+        Ready();

+        return S_OK;

+    }

+

+    // Have we run off the end of stream

+

+    if (IsEndOfStream() == TRUE) {

+        Ready();

+        return S_OK;

+    }

+

+    // Make sure we get fresh data after being stopped

+

+    if (HaveCurrentSample() == TRUE) {

+        if (OldState != State_Stopped) {

+            Ready();

+            return S_OK;

+        }

+    }

+    NotReady();

+    return S_FALSE;

+}

+

+

+// When we stop the filter the things we do are:-

+

+//      Decommit the allocator being used in the connection

+//      Release the source filter if it's waiting in Receive

+//      Cancel any advise link we set up with the clock

+//      Any end of stream signalled is now obsolete so reset

+//      Allow us to be stopped when we are not connected

+

+STDMETHODIMP CBaseRenderer::Stop()

+{

+    CAutoLock cRendererLock(&m_InterfaceLock);

+

+    // Make sure there really is a state change

+

+    if (m_State == State_Stopped) {

+        return NOERROR;

+    }

+

+    // Is our input pin connected

+

+    if (m_pInputPin->IsConnected() == FALSE) {

+        NOTE("Input pin is not connected");

+        m_State = State_Stopped;

+        return NOERROR;

+    }

+

+    CBaseFilter::Stop();

+

+    // If we are going into a stopped state then we must decommit whatever

+    // allocator we are using it so that any source filter waiting in the

+    // GetBuffer can be released and unlock themselves for a state change

+

+    if (m_pInputPin->Allocator()) {

+        m_pInputPin->Allocator()->Decommit();

+    }

+

+    // Cancel any scheduled rendering

+

+    SetRepaintStatus(TRUE);

+    StopStreaming();

+    SourceThreadCanWait(FALSE);

+    ResetEndOfStream();

+    CancelNotification();

+

+    // There should be no outstanding clock advise

+    ASSERT(CancelNotification() == S_FALSE);

+    ASSERT(WAIT_TIMEOUT == WaitForSingleObject((HANDLE)m_RenderEvent,0));

+    ASSERT(m_EndOfStreamTimer == 0);

+

+    Ready();

+    WaitForReceiveToComplete();

+    m_bAbort = FALSE;

+

+    return NOERROR;

+}

+

+

+// When we pause the filter the things we do are:-

+

+//      Commit the allocator being used in the connection

+//      Allow a source filter thread to wait in Receive

+//      Cancel any clock advise link (we may be running)

+//      Possibly complete the state change if we have data

+//      Allow us to be paused when we are not connected

+

+STDMETHODIMP CBaseRenderer::Pause()

+{

+    CAutoLock cRendererLock(&m_InterfaceLock);

+    FILTER_STATE OldState = m_State;

+    ASSERT(m_pInputPin->IsFlushing() == FALSE);

+

+    // Make sure there really is a state change

+

+    if (m_State == State_Paused) {

+        return CompleteStateChange(State_Paused);

+    }

+

+    // Has our input pin been connected

+

+    if (m_pInputPin->IsConnected() == FALSE) {

+        NOTE("Input pin is not connected");

+        m_State = State_Paused;

+        return CompleteStateChange(State_Paused);

+    }

+

+    // Pause the base filter class

+

+    HRESULT hr = CBaseFilter::Pause();

+    if (FAILED(hr)) {

+        NOTE("Pause failed");

+        return hr;

+    }

+

+    // Enable EC_REPAINT events again

+

+    SetRepaintStatus(TRUE);

+    StopStreaming();

+    SourceThreadCanWait(TRUE);

+    CancelNotification();

+    ResetEndOfStreamTimer();

+

+    // If we are going into a paused state then we must commit whatever

+    // allocator we are using it so that any source filter can call the

+    // GetBuffer and expect to get a buffer without returning an error

+

+    if (m_pInputPin->Allocator()) {

+        m_pInputPin->Allocator()->Commit();

+    }

+

+    // There should be no outstanding advise

+    ASSERT(CancelNotification() == S_FALSE);

+    ASSERT(WAIT_TIMEOUT == WaitForSingleObject((HANDLE)m_RenderEvent,0));

+    ASSERT(m_EndOfStreamTimer == 0);

+    ASSERT(m_pInputPin->IsFlushing() == FALSE);

+

+    // When we come out of a stopped state we must clear any image we were

+    // holding onto for frame refreshing. Since renderers see state changes

+    // first we can reset ourselves ready to accept the source thread data

+    // Paused or running after being stopped causes the current position to

+    // be reset so we're not interested in passing end of stream signals

+

+    if (OldState == State_Stopped) {

+        m_bAbort = FALSE;

+        ClearPendingSample();

+    }

+    return CompleteStateChange(OldState);

+}

+

+

+// When we run the filter the things we do are:-

+

+//      Commit the allocator being used in the connection

+//      Allow a source filter thread to wait in Receive

+//      Signal the render event just to get us going

+//      Start the base class by calling StartStreaming

+//      Allow us to be run when we are not connected

+//      Signal EC_COMPLETE if we are not connected

+

+STDMETHODIMP CBaseRenderer::Run(REFERENCE_TIME StartTime)

+{

+    CAutoLock cRendererLock(&m_InterfaceLock);

+    FILTER_STATE OldState = m_State;

+

+    // Make sure there really is a state change

+

+    if (m_State == State_Running) {

+        return NOERROR;

+    }

+

+    // Send EC_COMPLETE if we're not connected

+

+    if (m_pInputPin->IsConnected() == FALSE) {

+        NotifyEvent(EC_COMPLETE,S_OK,(LONG_PTR)(IBaseFilter *)this);

+        m_State = State_Running;

+        return NOERROR;

+    }

+

+    Ready();

+

+    // Pause the base filter class

+

+    HRESULT hr = CBaseFilter::Run(StartTime);

+    if (FAILED(hr)) {

+        NOTE("Run failed");

+        return hr;

+    }

+

+    // Allow the source thread to wait

+    ASSERT(m_pInputPin->IsFlushing() == FALSE);

+    SourceThreadCanWait(TRUE);

+    SetRepaintStatus(FALSE);

+

+    // There should be no outstanding advise

+    ASSERT(CancelNotification() == S_FALSE);

+    ASSERT(WAIT_TIMEOUT == WaitForSingleObject((HANDLE)m_RenderEvent,0));

+    ASSERT(m_EndOfStreamTimer == 0);

+    ASSERT(m_pInputPin->IsFlushing() == FALSE);

+

+    // If we are going into a running state then we must commit whatever

+    // allocator we are using it so that any source filter can call the

+    // GetBuffer and expect to get a buffer without returning an error

+

+    if (m_pInputPin->Allocator()) {

+        m_pInputPin->Allocator()->Commit();

+    }

+

+    // When we come out of a stopped state we must clear any image we were

+    // holding onto for frame refreshing. Since renderers see state changes

+    // first we can reset ourselves ready to accept the source thread data

+    // Paused or running after being stopped causes the current position to

+    // be reset so we're not interested in passing end of stream signals

+

+    if (OldState == State_Stopped) {

+        m_bAbort = FALSE;

+        ClearPendingSample();

+    }

+    return StartStreaming();

+}

+

+

+// Return the number of input pins we support

+

+int CBaseRenderer::GetPinCount()

+{

+    if (m_pInputPin == NULL) {

+        //  Try to create it

+        (void)GetPin(0);

+    }

+    return m_pInputPin != NULL ? 1 : 0;

+}

+

+

+// We only support one input pin and it is numbered zero

+

+CBasePin *CBaseRenderer::GetPin(int n)

+{

+    CAutoLock cObjectCreationLock(&m_ObjectCreationLock);

+

+    // Should only ever be called with zero

+    ASSERT(n == 0);

+

+    if (n != 0) {

+        return NULL;

+    }

+

+    // Create the input pin if not already done so

+

+    if (m_pInputPin == NULL) {

+

+        // hr must be initialized to NOERROR because

+        // CRendererInputPin's constructor only changes

+        // hr's value if an error occurs.

+        HRESULT hr = NOERROR;

+

+        m_pInputPin = new CRendererInputPin(this,&hr,L"In");

+        if (NULL == m_pInputPin) {

+            return NULL;

+        }

+

+        if (FAILED(hr)) {

+            delete m_pInputPin;

+            m_pInputPin = NULL;

+            return NULL;

+        }

+    }

+    return m_pInputPin;

+}

+

+

+// If "In" then return the IPin for our input pin, otherwise NULL and error

+

+STDMETHODIMP CBaseRenderer::FindPin(LPCWSTR Id, __deref_out IPin **ppPin)

+{

+    CheckPointer(ppPin,E_POINTER);

+

+    if (0==lstrcmpW(Id,L"In")) {

+        *ppPin = GetPin(0);

+        if (*ppPin) {

+            (*ppPin)->AddRef();

+        } else {

+            return E_OUTOFMEMORY;

+        }

+    } else {

+        *ppPin = NULL;

+        return VFW_E_NOT_FOUND;

+    }

+    return NOERROR;

+}

+

+

+// Called when the input pin receives an EndOfStream notification. If we have

+// not got a sample, then notify EC_COMPLETE now. If we have samples, then set

+// m_bEOS and check for this on completing samples. If we're waiting to pause

+// then complete the transition to paused state by setting the state event

+

+HRESULT CBaseRenderer::EndOfStream()

+{

+    // Ignore these calls if we are stopped

+

+    if (m_State == State_Stopped) {

+        return NOERROR;

+    }

+

+    // If we have a sample then wait for it to be rendered

+

+    m_bEOS = TRUE;

+    if (m_pMediaSample) {

+        return NOERROR;

+    }

+

+    // If we are waiting for pause then we are now ready since we cannot now

+    // carry on waiting for a sample to arrive since we are being told there

+    // won't be any. This sets an event that the GetState function picks up

+

+    Ready();

+

+    // Only signal completion now if we are running otherwise queue it until

+    // we do run in StartStreaming. This is used when we seek because a seek

+    // causes a pause where early notification of completion is misleading

+

+    if (m_bStreaming) {

+        SendEndOfStream();

+    }

+    return NOERROR;

+}

+

+

+// When we are told to flush we should release the source thread

+

+HRESULT CBaseRenderer::BeginFlush()

+{

+    // If paused then report state intermediate until we get some data

+

+    if (m_State == State_Paused) {

+        NotReady();

+    }

+

+    SourceThreadCanWait(FALSE);

+    CancelNotification();

+    ClearPendingSample();

+    //  Wait for Receive to complete

+    WaitForReceiveToComplete();

+

+    return NOERROR;

+}

+

+

+// After flushing the source thread can wait in Receive again

+

+HRESULT CBaseRenderer::EndFlush()

+{

+    // Reset the current sample media time

+    if (m_pPosition) m_pPosition->ResetMediaTime();

+

+    // There should be no outstanding advise

+

+    ASSERT(CancelNotification() == S_FALSE);

+    SourceThreadCanWait(TRUE);

+    return NOERROR;

+}

+

+

+// We can now send EC_REPAINTs if so required

+

+HRESULT CBaseRenderer::CompleteConnect(IPin *pReceivePin)

+{

+    // The caller should always hold the interface lock because

+    // the function uses CBaseFilter::m_State.

+    ASSERT(CritCheckIn(&m_InterfaceLock));

+

+    m_bAbort = FALSE;

+

+    if (State_Running == GetRealState()) {

+        HRESULT hr = StartStreaming();

+        if (FAILED(hr)) {

+            return hr;

+        }

+

+        SetRepaintStatus(FALSE);

+    } else {

+        SetRepaintStatus(TRUE);

+    }

+

+    return NOERROR;

+}

+

+

+// Called when we go paused or running

+

+HRESULT CBaseRenderer::Active()

+{

+    return NOERROR;

+}

+

+

+// Called when we go into a stopped state

+

+HRESULT CBaseRenderer::Inactive()

+{

+    if (m_pPosition) {

+        m_pPosition->ResetMediaTime();

+    }

+    //  People who derive from this may want to override this behaviour

+    //  to keep hold of the sample in some circumstances

+    ClearPendingSample();

+

+    return NOERROR;

+}

+

+

+// Tell derived classes about the media type agreed

+

+HRESULT CBaseRenderer::SetMediaType(const CMediaType *pmt)

+{

+    return NOERROR;

+}

+

+

+// When we break the input pin connection we should reset the EOS flags. When

+// we are asked for either IMediaPosition or IMediaSeeking we will create a

+// CPosPassThru object to handles media time pass through. When we're handed

+// samples we store (by calling CPosPassThru::RegisterMediaTime) their media

+// times so we can then return a real current position of data being rendered

+

+HRESULT CBaseRenderer::BreakConnect()

+{

+    // Do we have a quality management sink

+

+    if (m_pQSink) {

+        m_pQSink->Release();

+        m_pQSink = NULL;

+    }

+

+    // Check we have a valid connection

+

+    if (m_pInputPin->IsConnected() == FALSE) {

+        return S_FALSE;

+    }

+

+    // Check we are stopped before disconnecting

+    if (m_State != State_Stopped && !m_pInputPin->CanReconnectWhenActive()) {

+        return VFW_E_NOT_STOPPED;

+    }

+

+    SetRepaintStatus(FALSE);

+    ResetEndOfStream();

+    ClearPendingSample();

+    m_bAbort = FALSE;

+

+    if (State_Running == m_State) {

+        StopStreaming();

+    }

+

+    return NOERROR;

+}

+

+

+// Retrieves the sample times for this samples (note the sample times are

+// passed in by reference not value). We return S_FALSE to say schedule this

+// sample according to the times on the sample. We also return S_OK in

+// which case the object should simply render the sample data immediately

+

+HRESULT CBaseRenderer::GetSampleTimes(IMediaSample *pMediaSample,

+                                      __out REFERENCE_TIME *pStartTime,

+                                      __out REFERENCE_TIME *pEndTime)

+{

+    ASSERT(m_dwAdvise == 0);

+    ASSERT(pMediaSample);

+

+    // If the stop time for this sample is before or the same as start time,

+    // then just ignore it (release it) and schedule the next one in line

+    // Source filters should always fill in the start and end times properly!

+

+    if (SUCCEEDED(pMediaSample->GetTime(pStartTime, pEndTime))) {

+        if (*pEndTime < *pStartTime) {

+            return VFW_E_START_TIME_AFTER_END;

+        }

+    } else {

+        // no time set in the sample... draw it now?

+        return S_OK;

+    }

+

+    // Can't synchronise without a clock so we return S_OK which tells the

+    // caller that the sample should be rendered immediately without going

+    // through the overhead of setting a timer advise link with the clock

+

+    if (m_pClock == NULL) {

+        return S_OK;

+    }

+    return ShouldDrawSampleNow(pMediaSample,pStartTime,pEndTime);

+}

+

+

+// By default all samples are drawn according to their time stamps so we

+// return S_FALSE. Returning S_OK means draw immediately, this is used

+// by the derived video renderer class in its quality management.

+

+HRESULT CBaseRenderer::ShouldDrawSampleNow(IMediaSample *pMediaSample,

+                                           __out REFERENCE_TIME *ptrStart,

+                                           __out REFERENCE_TIME *ptrEnd)

+{

+    return S_FALSE;

+}

+

+

+// We must always reset the current advise time to zero after a timer fires

+// because there are several possible ways which lead us not to do any more

+// scheduling such as the pending image being cleared after state changes

+

+void CBaseRenderer::SignalTimerFired()

+{

+    m_dwAdvise = 0;

+}

+

+

+// Cancel any notification currently scheduled. This is called by the owning

+// window object when it is told to stop streaming. If there is no timer link

+// outstanding then calling this is benign otherwise we go ahead and cancel

+// We must always reset the render event as the quality management code can

+// signal immediate rendering by setting the event without setting an advise

+// link. If we're subsequently stopped and run the first attempt to setup an

+// advise link with the reference clock will find the event still signalled

+

+HRESULT CBaseRenderer::CancelNotification()

+{

+    ASSERT(m_dwAdvise == 0 || m_pClock);

+    DWORD_PTR dwAdvise = m_dwAdvise;

+

+    // Have we a live advise link

+

+    if (m_dwAdvise) {

+        m_pClock->Unadvise(m_dwAdvise);

+        SignalTimerFired();

+        ASSERT(m_dwAdvise == 0);

+    }

+

+    // Clear the event and return our status

+

+    m_RenderEvent.Reset();

+    return (dwAdvise ? S_OK : S_FALSE);

+}

+

+

+// Responsible for setting up one shot advise links with the clock

+// Return FALSE if the sample is to be dropped (not drawn at all)

+// Return TRUE if the sample is to be drawn and in this case also

+// arrange for m_RenderEvent to be set at the appropriate time

+

+BOOL CBaseRenderer::ScheduleSample(IMediaSample *pMediaSample)

+{

+    REFERENCE_TIME StartSample, EndSample;

+

+    // Is someone pulling our leg

+

+    if (pMediaSample == NULL) {

+        return FALSE;

+    }

+

+    // Get the next sample due up for rendering.  If there aren't any ready

+    // then GetNextSampleTimes returns an error.  If there is one to be done

+    // then it succeeds and yields the sample times. If it is due now then

+    // it returns S_OK other if it's to be done when due it returns S_FALSE

+

+    HRESULT hr = GetSampleTimes(pMediaSample, &StartSample, &EndSample);

+    if (FAILED(hr)) {

+        return FALSE;

+    }

+

+    // If we don't have a reference clock then we cannot set up the advise

+    // time so we simply set the event indicating an image to render. This

+    // will cause us to run flat out without any timing or synchronisation

+

+    if (hr == S_OK) {

+        EXECUTE_ASSERT(SetEvent((HANDLE) m_RenderEvent));

+        return TRUE;

+    }

+

+    ASSERT(m_dwAdvise == 0);

+    ASSERT(m_pClock);

+    ASSERT(WAIT_TIMEOUT == WaitForSingleObject((HANDLE)m_RenderEvent,0));

+

+    // We do have a valid reference clock interface so we can ask it to

+    // set an event when the image comes due for rendering. We pass in

+    // the reference time we were told to start at and also the current

+    // stream time which is the offset from the start reference time

+

+    hr = m_pClock->AdviseTime(

+            (REFERENCE_TIME) m_tStart,          // Start run time

+            StartSample,                        // Stream time

+            (HEVENT)(HANDLE) m_RenderEvent,     // Render notification

+            &m_dwAdvise);                       // Advise cookie

+

+    if (SUCCEEDED(hr)) {

+        return TRUE;

+    }

+

+    // We could not schedule the next sample for rendering despite the fact

+    // we have a valid sample here. This is a fair indication that either

+    // the system clock is wrong or the time stamp for the sample is duff

+

+    ASSERT(m_dwAdvise == 0);

+    return FALSE;

+}

+

+

+// This is called when a sample comes due for rendering. We pass the sample

+// on to the derived class. After rendering we will initialise the timer for

+// the next sample, NOTE signal that the last one fired first, if we don't

+// do this it thinks there is still one outstanding that hasn't completed

+

+HRESULT CBaseRenderer::Render(IMediaSample *pMediaSample)

+{

+    // If the media sample is NULL then we will have been notified by the

+    // clock that another sample is ready but in the mean time someone has

+    // stopped us streaming which causes the next sample to be released

+

+    if (pMediaSample == NULL) {

+        return S_FALSE;

+    }

+

+    // If we have stopped streaming then don't render any more samples, the

+    // thread that got in and locked us and then reset this flag does not

+    // clear the pending sample as we can use it to refresh any output device

+

+    if (m_bStreaming == FALSE) {

+        return S_FALSE;

+    }

+

+    // Time how long the rendering takes

+

+    OnRenderStart(pMediaSample);

+    DoRenderSample(pMediaSample);

+    OnRenderEnd(pMediaSample);

+

+    return NOERROR;

+}

+

+

+// Checks if there is a sample waiting at the renderer

+

+BOOL CBaseRenderer::HaveCurrentSample()

+{

+    CAutoLock cRendererLock(&m_RendererLock);

+    return (m_pMediaSample == NULL ? FALSE : TRUE);

+}

+

+

+// Returns the current sample waiting at the video renderer. We AddRef the

+// sample before returning so that should it come due for rendering the

+// person who called this method will hold the remaining reference count

+// that will stop the sample being added back onto the allocator free list

+

+IMediaSample *CBaseRenderer::GetCurrentSample()

+{

+    CAutoLock cRendererLock(&m_RendererLock);

+    if (m_pMediaSample) {

+        m_pMediaSample->AddRef();

+    }

+    return m_pMediaSample;

+}

+

+

+// Called when the source delivers us a sample. We go through a few checks to

+// make sure the sample can be rendered. If we are running (streaming) then we

+// have the sample scheduled with the reference clock, if we are not streaming

+// then we have received an sample in paused mode so we can complete any state

+// transition. On leaving this function everything will be unlocked so an app

+// thread may get in and change our state to stopped (for example) in which

+// case it will also signal the thread event so that our wait call is stopped

+

+HRESULT CBaseRenderer::PrepareReceive(IMediaSample *pMediaSample)

+{

+    CAutoLock cInterfaceLock(&m_InterfaceLock);

+    m_bInReceive = TRUE;

+

+    // Check our flushing and filter state

+

+    // This function must hold the interface lock because it calls 

+    // CBaseInputPin::Receive() and CBaseInputPin::Receive() uses

+    // CBasePin::m_bRunTimeError.

+    HRESULT hr = m_pInputPin->CBaseInputPin::Receive(pMediaSample);

+

+    if (hr != NOERROR) {

+        m_bInReceive = FALSE;

+        return E_FAIL;

+    }

+

+    // Has the type changed on a media sample. We do all rendering

+    // synchronously on the source thread, which has a side effect

+    // that only one buffer is ever outstanding. Therefore when we

+    // have Receive called we can go ahead and change the format

+    // Since the format change can cause a SendMessage we just don't

+    // lock

+    if (m_pInputPin->SampleProps()->pMediaType) {

+        hr = m_pInputPin->SetMediaType(

+                (CMediaType *)m_pInputPin->SampleProps()->pMediaType);

+        if (FAILED(hr)) {

+            m_bInReceive = FALSE;

+            return hr;

+        }

+    }

+

+

+    CAutoLock cSampleLock(&m_RendererLock);

+

+    ASSERT(IsActive() == TRUE);

+    ASSERT(m_pInputPin->IsFlushing() == FALSE);

+    ASSERT(m_pInputPin->IsConnected() == TRUE);

+    ASSERT(m_pMediaSample == NULL);

+

+    // Return an error if we already have a sample waiting for rendering

+    // source pins must serialise the Receive calls - we also check that

+    // no data is being sent after the source signalled an end of stream

+

+    if (m_pMediaSample || m_bEOS || m_bAbort) {

+        Ready();

+        m_bInReceive = FALSE;

+        return E_UNEXPECTED;

+    }

+

+    // Store the media times from this sample

+    if (m_pPosition) m_pPosition->RegisterMediaTime(pMediaSample);

+

+    // Schedule the next sample if we are streaming

+

+    if ((m_bStreaming == TRUE) && (ScheduleSample(pMediaSample) == FALSE)) {

+        ASSERT(WAIT_TIMEOUT == WaitForSingleObject((HANDLE)m_RenderEvent,0));

+        ASSERT(CancelNotification() == S_FALSE);

+        m_bInReceive = FALSE;

+        return VFW_E_SAMPLE_REJECTED;

+    }

+

+    // Store the sample end time for EC_COMPLETE handling

+    m_SignalTime = m_pInputPin->SampleProps()->tStop;

+

+    // BEWARE we sometimes keep the sample even after returning the thread to

+    // the source filter such as when we go into a stopped state (we keep it

+    // to refresh the device with) so we must AddRef it to keep it safely. If

+    // we start flushing the source thread is released and any sample waiting

+    // will be released otherwise GetBuffer may never return (see BeginFlush)

+

+    m_pMediaSample = pMediaSample;

+    m_pMediaSample->AddRef();

+

+    if (m_bStreaming == FALSE) {

+        SetRepaintStatus(TRUE);

+    }

+    return NOERROR;

+}

+

+

+// Called by the source filter when we have a sample to render. Under normal

+// circumstances we set an advise link with the clock, wait for the time to

+// arrive and then render the data using the PURE virtual DoRenderSample that

+// the derived class will have overriden. After rendering the sample we may

+// also signal EOS if it was the last one sent before EndOfStream was called

+

+HRESULT CBaseRenderer::Receive(IMediaSample *pSample)

+{

+    ASSERT(pSample);

+

+    // It may return VFW_E_SAMPLE_REJECTED code to say don't bother

+

+    HRESULT hr = PrepareReceive(pSample);

+    ASSERT(m_bInReceive == SUCCEEDED(hr));

+    if (FAILED(hr)) {

+        if (hr == VFW_E_SAMPLE_REJECTED) {

+            return NOERROR;

+        }

+        return hr;

+    }

+

+    // We realize the palette in "PrepareRender()" so we have to give away the

+    // filter lock here.

+    if (m_State == State_Paused) {

+        PrepareRender();

+        // no need to use InterlockedExchange

+        m_bInReceive = FALSE;

+        {

+            // We must hold both these locks

+            CAutoLock cRendererLock(&m_InterfaceLock);

+            if (m_State == State_Stopped)

+                return NOERROR;

+

+            m_bInReceive = TRUE;

+            CAutoLock cSampleLock(&m_RendererLock);

+            OnReceiveFirstSample(pSample);

+        }

+        Ready();

+    }

+    // Having set an advise link with the clock we sit and wait. We may be

+    // awoken by the clock firing or by a state change. The rendering call

+    // will lock the critical section and check we can still render the data

+

+    hr = WaitForRenderTime();

+    if (FAILED(hr)) {

+        m_bInReceive = FALSE;

+        return NOERROR;

+    }

+

+    PrepareRender();

+

+    //  Set this here and poll it until we work out the locking correctly

+    //  It can't be right that the streaming stuff grabs the interface

+    //  lock - after all we want to be able to wait for this stuff

+    //  to complete

+    m_bInReceive = FALSE;

+

+    // We must hold both these locks

+    CAutoLock cRendererLock(&m_InterfaceLock);

+

+    // since we gave away the filter wide lock, the sate of the filter could

+    // have chnaged to Stopped

+    if (m_State == State_Stopped)

+        return NOERROR;

+

+    CAutoLock cSampleLock(&m_RendererLock);

+

+    // Deal with this sample

+

+    Render(m_pMediaSample);

+    ClearPendingSample();

+    SendEndOfStream();

+    CancelNotification();

+    return NOERROR;

+}

+

+

+// This is called when we stop or are inactivated to clear the pending sample

+// We release the media sample interface so that they can be allocated to the

+// source filter again, unless of course we are changing state to inactive in

+// which case GetBuffer will return an error. We must also reset the current

+// media sample to NULL so that we know we do not currently have an image

+

+HRESULT CBaseRenderer::ClearPendingSample()

+{

+    CAutoLock cRendererLock(&m_RendererLock);

+    if (m_pMediaSample) {

+        m_pMediaSample->Release();

+        m_pMediaSample = NULL;

+    }

+    return NOERROR;

+}

+

+

+// Used to signal end of stream according to the sample end time

+

+void CALLBACK EndOfStreamTimer(UINT uID,        // Timer identifier

+                               UINT uMsg,       // Not currently used

+                               DWORD_PTR dwUser,// User information

+                               DWORD_PTR dw1,   // Windows reserved

+                               DWORD_PTR dw2)   // is also reserved

+{

+    CBaseRenderer *pRenderer = (CBaseRenderer *) dwUser;

+    NOTE1("EndOfStreamTimer called (%d)",uID);

+    pRenderer->TimerCallback();

+}

+

+//  Do the timer callback work

+void CBaseRenderer::TimerCallback()

+{

+    //  Lock for synchronization (but don't hold this lock when calling

+    //  timeKillEvent)

+    CAutoLock cRendererLock(&m_RendererLock);

+

+    // See if we should signal end of stream now

+

+    if (m_EndOfStreamTimer) {

+        m_EndOfStreamTimer = 0;

+        SendEndOfStream();

+    }

+}

+

+

+// If we are at the end of the stream signal the filter graph but do not set

+// the state flag back to FALSE. Once we drop off the end of the stream we

+// leave the flag set (until a subsequent ResetEndOfStream). Each sample we

+// get delivered will update m_SignalTime to be the last sample's end time.

+// We must wait this long before signalling end of stream to the filtergraph

+

+#define TIMEOUT_DELIVERYWAIT 50

+#define TIMEOUT_RESOLUTION 10

+

+HRESULT CBaseRenderer::SendEndOfStream()

+{

+    ASSERT(CritCheckIn(&m_RendererLock));

+    if (m_bEOS == FALSE || m_bEOSDelivered || m_EndOfStreamTimer) {

+        return NOERROR;

+    }

+

+    // If there is no clock then signal immediately

+    if (m_pClock == NULL) {

+        return NotifyEndOfStream();

+    }

+

+    // How long into the future is the delivery time

+

+    REFERENCE_TIME Signal = m_tStart + m_SignalTime;

+    REFERENCE_TIME CurrentTime;

+    m_pClock->GetTime(&CurrentTime);

+    LONG Delay = LONG((Signal - CurrentTime) / 10000);

+

+    // Dump the timing information to the debugger

+

+    NOTE1("Delay until end of stream delivery %d",Delay);

+    NOTE1("Current %s",(LPCTSTR)CDisp((LONGLONG)CurrentTime));

+    NOTE1("Signal %s",(LPCTSTR)CDisp((LONGLONG)Signal));

+

+    // Wait for the delivery time to arrive

+

+    if (Delay < TIMEOUT_DELIVERYWAIT) {

+        return NotifyEndOfStream();

+    }

+

+    // Signal a timer callback on another worker thread

+

+    m_EndOfStreamTimer = CompatibleTimeSetEvent((UINT) Delay, // Period of timer

+                                      TIMEOUT_RESOLUTION,     // Timer resolution

+                                      EndOfStreamTimer,       // Callback function

+                                      DWORD_PTR(this),        // Used information

+                                      TIME_ONESHOT);          // Type of callback

+    if (m_EndOfStreamTimer == 0) {

+        return NotifyEndOfStream();

+    }

+    return NOERROR;

+}

+

+

+// Signals EC_COMPLETE to the filtergraph manager

+

+HRESULT CBaseRenderer::NotifyEndOfStream()

+{

+    CAutoLock cRendererLock(&m_RendererLock);

+    ASSERT(m_bEOSDelivered == FALSE);

+    ASSERT(m_EndOfStreamTimer == 0);

+

+    // Has the filter changed state

+

+    if (m_bStreaming == FALSE) {

+        ASSERT(m_EndOfStreamTimer == 0);

+        return NOERROR;

+    }

+

+    // Reset the end of stream timer

+    m_EndOfStreamTimer = 0;

+

+    // If we've been using the IMediaPosition interface, set it's start

+    // and end media "times" to the stop position by hand.  This ensures

+    // that we actually get to the end, even if the MPEG guestimate has

+    // been bad or if the quality management dropped the last few frames

+

+    if (m_pPosition) m_pPosition->EOS();

+    m_bEOSDelivered = TRUE;

+    NOTE("Sending EC_COMPLETE...");

+    return NotifyEvent(EC_COMPLETE,S_OK,(LONG_PTR)(IBaseFilter *)this);

+}

+

+

+// Reset the end of stream flag, this is typically called when we transfer to

+// stopped states since that resets the current position back to the start so

+// we will receive more samples or another EndOfStream if there aren't any. We

+// keep two separate flags one to say we have run off the end of the stream

+// (this is the m_bEOS flag) and another to say we have delivered EC_COMPLETE

+// to the filter graph. We need the latter otherwise we can end up sending an

+// EC_COMPLETE every time the source changes state and calls our EndOfStream

+

+HRESULT CBaseRenderer::ResetEndOfStream()

+{

+    ResetEndOfStreamTimer();

+    CAutoLock cRendererLock(&m_RendererLock);

+

+    m_bEOS = FALSE;

+    m_bEOSDelivered = FALSE;

+    m_SignalTime = 0;

+

+    return NOERROR;

+}

+

+

+// Kills any outstanding end of stream timer

+

+void CBaseRenderer::ResetEndOfStreamTimer()

+{

+    ASSERT(CritCheckOut(&m_RendererLock));

+    if (m_EndOfStreamTimer) {

+        timeKillEvent(m_EndOfStreamTimer);

+        m_EndOfStreamTimer = 0;

+    }

+}

+

+

+// This is called when we start running so that we can schedule any pending

+// image we have with the clock and display any timing information. If we

+// don't have any sample but we have queued an EOS flag then we send it. If

+// we do have a sample then we wait until that has been rendered before we

+// signal the filter graph otherwise we may change state before it's done

+

+HRESULT CBaseRenderer::StartStreaming()

+{

+    CAutoLock cRendererLock(&m_RendererLock);

+    if (m_bStreaming == TRUE) {

+        return NOERROR;

+    }

+

+    // Reset the streaming times ready for running

+

+    m_bStreaming = TRUE;

+

+    timeBeginPeriod(1);

+    OnStartStreaming();

+

+    // There should be no outstanding advise

+    ASSERT(WAIT_TIMEOUT == WaitForSingleObject((HANDLE)m_RenderEvent,0));

+    ASSERT(CancelNotification() == S_FALSE);

+

+    // If we have an EOS and no data then deliver it now

+

+    if (m_pMediaSample == NULL) {

+        return SendEndOfStream();

+    }

+

+    // Have the data rendered

+

+    ASSERT(m_pMediaSample);

+    if (!ScheduleSample(m_pMediaSample))

+        m_RenderEvent.Set();

+

+    return NOERROR;

+}

+

+

+// This is called when we stop streaming so that we can set our internal flag

+// indicating we are not now to schedule any more samples arriving. The state

+// change methods in the filter implementation take care of cancelling any

+// clock advise link we have set up and clearing any pending sample we have

+

+HRESULT CBaseRenderer::StopStreaming()

+{

+    CAutoLock cRendererLock(&m_RendererLock);

+    m_bEOSDelivered = FALSE;

+

+    if (m_bStreaming == TRUE) {

+        m_bStreaming = FALSE;

+        OnStopStreaming();

+        timeEndPeriod(1);

+    }

+    return NOERROR;

+}

+

+

+// We have a boolean flag that is reset when we have signalled EC_REPAINT to

+// the filter graph. We set this when we receive an image so that should any

+// conditions arise again we can send another one. By having a flag we ensure

+// we don't flood the filter graph with redundant calls. We do not set the

+// event when we receive an EndOfStream call since there is no point in us

+// sending further EC_REPAINTs. In particular the AutoShowWindow method and

+// the DirectDraw object use this method to control the window repainting

+

+void CBaseRenderer::SetRepaintStatus(BOOL bRepaint)

+{

+    CAutoLock cSampleLock(&m_RendererLock);

+    m_bRepaintStatus = bRepaint;

+}

+

+

+// Pass the window handle to the upstream filter

+

+void CBaseRenderer::SendNotifyWindow(IPin *pPin,HWND hwnd)

+{

+    IMediaEventSink *pSink;

+

+    // Does the pin support IMediaEventSink

+    HRESULT hr = pPin->QueryInterface(IID_IMediaEventSink,(void **)&pSink);

+    if (SUCCEEDED(hr)) {

+        pSink->Notify(EC_NOTIFY_WINDOW,LONG_PTR(hwnd),0);

+        pSink->Release();

+    }

+    NotifyEvent(EC_NOTIFY_WINDOW,LONG_PTR(hwnd),0);

+}

+

+

+// Signal an EC_REPAINT to the filter graph. This can be used to have data

+// sent to us. For example when a video window is first displayed it may

+// not have an image to display, at which point it signals EC_REPAINT. The

+// filtergraph will either pause the graph if stopped or if already paused

+// it will call put_CurrentPosition of the current position. Setting the

+// current position to itself has the stream flushed and the image resent

+

+#define RLOG(_x_) DbgLog((LOG_TRACE,1,TEXT(_x_)));

+

+void CBaseRenderer::SendRepaint()

+{

+    CAutoLock cSampleLock(&m_RendererLock);

+    ASSERT(m_pInputPin);

+

+    // We should not send repaint notifications when...

+    //    - An end of stream has been notified

+    //    - Our input pin is being flushed

+    //    - The input pin is not connected

+    //    - We have aborted a video playback

+    //    - There is a repaint already sent

+

+    if (m_bAbort == FALSE) {

+        if (m_pInputPin->IsConnected() == TRUE) {

+            if (m_pInputPin->IsFlushing() == FALSE) {

+                if (IsEndOfStream() == FALSE) {

+                    if (m_bRepaintStatus == TRUE) {

+                        IPin *pPin = (IPin *) m_pInputPin;

+                        NotifyEvent(EC_REPAINT,(LONG_PTR) pPin,0);

+                        SetRepaintStatus(FALSE);

+                        RLOG("Sending repaint");

+                    }

+                }

+            }

+        }

+    }

+}

+

+

+// When a video window detects a display change (WM_DISPLAYCHANGE message) it

+// can send an EC_DISPLAY_CHANGED event code along with the renderer pin. The

+// filtergraph will stop everyone and reconnect our input pin. As we're then

+// reconnected we can accept the media type that matches the new display mode

+// since we may no longer be able to draw the current image type efficiently

+

+BOOL CBaseRenderer::OnDisplayChange()

+{

+    // Ignore if we are not connected yet

+

+    CAutoLock cSampleLock(&m_RendererLock);

+    if (m_pInputPin->IsConnected() == FALSE) {

+        return FALSE;

+    }

+

+    RLOG("Notification of EC_DISPLAY_CHANGE");

+

+    // Pass our input pin as parameter on the event

+

+    IPin *pPin = (IPin *) m_pInputPin;

+    m_pInputPin->AddRef();

+    NotifyEvent(EC_DISPLAY_CHANGED,(LONG_PTR) pPin,0);

+    SetAbortSignal(TRUE);

+    ClearPendingSample();

+    m_pInputPin->Release();

+

+    return TRUE;

+}

+

+

+// Called just before we start drawing.

+// Store the current time in m_trRenderStart to allow the rendering time to be

+// logged.  Log the time stamp of the sample and how late it is (neg is early)

+

+void CBaseRenderer::OnRenderStart(IMediaSample *pMediaSample)

+{

+#ifdef PERF

+    REFERENCE_TIME trStart, trEnd;

+    pMediaSample->GetTime(&trStart, &trEnd);

+

+    MSR_INTEGER(m_idBaseStamp, (int)trStart);     // dump low order 32 bits

+

+    m_pClock->GetTime(&m_trRenderStart);

+    MSR_INTEGER(0, (int)m_trRenderStart);

+    REFERENCE_TIME trStream;

+    trStream = m_trRenderStart-m_tStart;     // convert reftime to stream time

+    MSR_INTEGER(0,(int)trStream);

+

+    const int trLate = (int)(trStream - trStart);

+    MSR_INTEGER(m_idBaseAccuracy, trLate/10000);  // dump in mSec

+#endif

+

+} // OnRenderStart

+

+

+// Called directly after drawing an image.

+// calculate the time spent drawing and log it.

+

+void CBaseRenderer::OnRenderEnd(IMediaSample *pMediaSample)

+{

+#ifdef PERF

+    REFERENCE_TIME trNow;

+    m_pClock->GetTime(&trNow);

+    MSR_INTEGER(0,(int)trNow);

+    int t = (int)((trNow - m_trRenderStart)/10000);   // convert UNITS->msec

+    MSR_INTEGER(m_idBaseRenderTime, t);

+#endif

+} // OnRenderEnd

+

+

+

+

+// Constructor must be passed the base renderer object

+

+CRendererInputPin::CRendererInputPin(__inout CBaseRenderer *pRenderer,

+                                     __inout HRESULT *phr,

+                                     __in_opt LPCWSTR pPinName) :

+    CBaseInputPin(NAME("Renderer pin"),

+                  pRenderer,

+                  &pRenderer->m_InterfaceLock,

+                  (HRESULT *) phr,

+                  pPinName)

+{

+    m_pRenderer = pRenderer;

+    ASSERT(m_pRenderer);

+}

+

+

+// Signals end of data stream on the input pin

+

+STDMETHODIMP CRendererInputPin::EndOfStream()

+{

+    CAutoLock cRendererLock(&m_pRenderer->m_InterfaceLock);

+    CAutoLock cSampleLock(&m_pRenderer->m_RendererLock);

+

+    // Make sure we're streaming ok

+

+    HRESULT hr = CheckStreaming();

+    if (hr != NOERROR) {

+        return hr;

+    }

+

+    // Pass it onto the renderer

+

+    hr = m_pRenderer->EndOfStream();

+    if (SUCCEEDED(hr)) {

+        hr = CBaseInputPin::EndOfStream();

+    }

+    return hr;

+}

+

+

+// Signals start of flushing on the input pin - we do the final reset end of

+// stream with the renderer lock unlocked but with the interface lock locked

+// We must do this because we call timeKillEvent, our timer callback method

+// has to take the renderer lock to serialise our state. Therefore holding a

+// renderer lock when calling timeKillEvent could cause a deadlock condition

+

+STDMETHODIMP CRendererInputPin::BeginFlush()

+{

+    CAutoLock cRendererLock(&m_pRenderer->m_InterfaceLock);

+    {

+        CAutoLock cSampleLock(&m_pRenderer->m_RendererLock);

+        CBaseInputPin::BeginFlush();

+        m_pRenderer->BeginFlush();

+    }

+    return m_pRenderer->ResetEndOfStream();

+}

+

+

+// Signals end of flushing on the input pin

+

+STDMETHODIMP CRendererInputPin::EndFlush()

+{

+    CAutoLock cRendererLock(&m_pRenderer->m_InterfaceLock);

+    CAutoLock cSampleLock(&m_pRenderer->m_RendererLock);

+

+    HRESULT hr = m_pRenderer->EndFlush();

+    if (SUCCEEDED(hr)) {

+        hr = CBaseInputPin::EndFlush();

+    }

+    return hr;

+}

+

+

+// Pass the sample straight through to the renderer object

+

+STDMETHODIMP CRendererInputPin::Receive(IMediaSample *pSample)

+{

+    HRESULT hr = m_pRenderer->Receive(pSample);

+    if (FAILED(hr)) {

+

+        // A deadlock could occur if the caller holds the renderer lock and

+        // attempts to acquire the interface lock.

+        ASSERT(CritCheckOut(&m_pRenderer->m_RendererLock));

+

+        {

+            // The interface lock must be held when the filter is calling

+            // IsStopped() or IsFlushing().  The interface lock must also

+            // be held because the function uses m_bRunTimeError.

+            CAutoLock cRendererLock(&m_pRenderer->m_InterfaceLock);

+

+            // We do not report errors which occur while the filter is stopping,

+            // flushing or if the m_bAbort flag is set .  Errors are expected to 

+            // occur during these operations and the streaming thread correctly 

+            // handles the errors.  

+            if (!IsStopped() && !IsFlushing() && !m_pRenderer->m_bAbort && !m_bRunTimeError) {

+

+                // EC_ERRORABORT's first parameter is the error which caused

+                // the event and its' last parameter is 0.  See the Direct

+                // Show SDK documentation for more information.

+                m_pRenderer->NotifyEvent(EC_ERRORABORT,hr,0);

+

+                {

+                    CAutoLock alRendererLock(&m_pRenderer->m_RendererLock);

+                    if (m_pRenderer->IsStreaming() && !m_pRenderer->IsEndOfStreamDelivered()) {

+                        m_pRenderer->NotifyEndOfStream();

+                    }

+                }

+    

+                m_bRunTimeError = TRUE;

+            }

+        }

+    }

+

+    return hr;

+}

+

+

+// Called when the input pin is disconnected

+

+HRESULT CRendererInputPin::BreakConnect()

+{

+    HRESULT hr = m_pRenderer->BreakConnect();

+    if (FAILED(hr)) {

+        return hr;

+    }

+    return CBaseInputPin::BreakConnect();

+}

+

+

+// Called when the input pin is connected

+

+HRESULT CRendererInputPin::CompleteConnect(IPin *pReceivePin)

+{

+    HRESULT hr = m_pRenderer->CompleteConnect(pReceivePin);

+    if (FAILED(hr)) {

+        return hr;

+    }

+    return CBaseInputPin::CompleteConnect(pReceivePin);

+}

+

+

+// Give the pin id of our one and only pin

+

+STDMETHODIMP CRendererInputPin::QueryId(__deref_out LPWSTR *Id)

+{

+    CheckPointer(Id,E_POINTER);

+

+    const WCHAR szIn[] = L"In";

+

+    *Id = (LPWSTR)CoTaskMemAlloc(sizeof(szIn));

+    if (*Id == NULL) {

+        return E_OUTOFMEMORY;

+    }

+    CopyMemory(*Id, szIn, sizeof(szIn));

+    return NOERROR;

+}

+

+

+// Will the filter accept this media type

+

+HRESULT CRendererInputPin::CheckMediaType(const CMediaType *pmt)

+{

+    return m_pRenderer->CheckMediaType(pmt);

+}

+

+

+// Called when we go paused or running

+

+HRESULT CRendererInputPin::Active()

+{

+    return m_pRenderer->Active();

+}

+

+

+// Called when we go into a stopped state

+

+HRESULT CRendererInputPin::Inactive()

+{

+    // The caller must hold the interface lock because 

+    // this function uses m_bRunTimeError.

+    ASSERT(CritCheckIn(&m_pRenderer->m_InterfaceLock));

+

+    m_bRunTimeError = FALSE;

+

+    return m_pRenderer->Inactive();

+}

+

+

+// Tell derived classes about the media type agreed

+

+HRESULT CRendererInputPin::SetMediaType(const CMediaType *pmt)

+{

+    HRESULT hr = CBaseInputPin::SetMediaType(pmt);

+    if (FAILED(hr)) {

+        return hr;

+    }

+    return m_pRenderer->SetMediaType(pmt);

+}

+

+

+// We do not keep an event object to use when setting up a timer link with

+// the clock but are given a pointer to one by the owning object through the

+// SetNotificationObject method - this must be initialised before starting

+// We can override the default quality management process to have it always

+// draw late frames, this is currently done by having the following registry

+// key (actually an INI key) called DrawLateFrames set to 1 (default is 0)

+

+const TCHAR AMQUALITY[] = TEXT("ActiveMovie");

+const TCHAR DRAWLATEFRAMES[] = TEXT("DrawLateFrames");

+

+CBaseVideoRenderer::CBaseVideoRenderer(

+      REFCLSID RenderClass, // CLSID for this renderer

+      __in_opt LPCTSTR pName,         // Debug ONLY description

+      __inout_opt LPUNKNOWN pUnk,       // Aggregated owner object

+      __inout HRESULT *phr) :       // General OLE return code

+

+    CBaseRenderer(RenderClass,pName,pUnk,phr),

+    m_cFramesDropped(0),

+    m_cFramesDrawn(0),

+    m_bSupplierHandlingQuality(FALSE)

+{

+    ResetStreamingTimes();

+

+#ifdef PERF

+    m_idTimeStamp       = MSR_REGISTER(TEXT("Frame time stamp"));

+    m_idEarliness       = MSR_REGISTER(TEXT("Earliness fudge"));

+    m_idTarget          = MSR_REGISTER(TEXT("Target (mSec)"));

+    m_idSchLateTime     = MSR_REGISTER(TEXT("mSec late when scheduled"));

+    m_idDecision        = MSR_REGISTER(TEXT("Scheduler decision code"));

+    m_idQualityRate     = MSR_REGISTER(TEXT("Quality rate sent"));

+    m_idQualityTime     = MSR_REGISTER(TEXT("Quality time sent"));

+    m_idWaitReal        = MSR_REGISTER(TEXT("Render wait"));

+    // m_idWait            = MSR_REGISTER(TEXT("wait time recorded (msec)"));

+    m_idFrameAccuracy   = MSR_REGISTER(TEXT("Frame accuracy (msecs)"));

+    m_bDrawLateFrames = GetProfileInt(AMQUALITY, DRAWLATEFRAMES, FALSE);

+    //m_idSendQuality      = MSR_REGISTER(TEXT("Processing Quality message"));

+

+    m_idRenderAvg       = MSR_REGISTER(TEXT("Render draw time Avg"));

+    m_idFrameAvg        = MSR_REGISTER(TEXT("FrameAvg"));

+    m_idWaitAvg         = MSR_REGISTER(TEXT("WaitAvg"));

+    m_idDuration        = MSR_REGISTER(TEXT("Duration"));

+    m_idThrottle        = MSR_REGISTER(TEXT("Audio-video throttle wait"));

+    // m_idDebug           = MSR_REGISTER(TEXT("Debug stuff"));

+#endif // PERF

+} // Constructor

+

+

+// Destructor is just a placeholder

+

+CBaseVideoRenderer::~CBaseVideoRenderer()

+{

+    ASSERT(m_dwAdvise == 0);

+}

+

+

+// The timing functions in this class are called by the window object and by

+// the renderer's allocator.

+// The windows object calls timing functions as it receives media sample

+// images for drawing using GDI.

+// The allocator calls timing functions when it starts passing DCI/DirectDraw

+// surfaces which are not rendered in the same way; The decompressor writes

+// directly to the surface with no separate rendering, so those code paths

+// call direct into us.  Since we only ever hand out DCI/DirectDraw surfaces

+// when we have allocated one and only one image we know there cannot be any

+// conflict between the two.

+//

+// We use timeGetTime to return the timing counts we use (since it's relative

+// performance we are interested in rather than absolute compared to a clock)

+// The window object sets the accuracy of the system clock (normally 1ms) by

+// calling timeBeginPeriod/timeEndPeriod when it changes streaming states

+

+

+// Reset all times controlling streaming.

+// Set them so that

+// 1. Frames will not initially be dropped

+// 2. The first frame will definitely be drawn (achieved by saying that there

+//    has not ben a frame drawn for a long time).

+

+HRESULT CBaseVideoRenderer::ResetStreamingTimes()

+{

+    m_trLastDraw = -1000;     // set up as first frame since ages (1 sec) ago

+    m_tStreamingStart = timeGetTime();

+    m_trRenderAvg = 0;

+    m_trFrameAvg = -1;        // -1000 fps == "unset"

+    m_trDuration = 0;         // 0 - strange value

+    m_trRenderLast = 0;

+    m_trWaitAvg = 0;

+    m_tRenderStart = 0;

+    m_cFramesDrawn = 0;

+    m_cFramesDropped = 0;

+    m_iTotAcc = 0;

+    m_iSumSqAcc = 0;

+    m_iSumSqFrameTime = 0;

+    m_trFrame = 0;          // hygeine - not really needed

+    m_trLate = 0;           // hygeine - not really needed

+    m_iSumFrameTime = 0;

+    m_nNormal = 0;

+    m_trEarliness = 0;

+    m_trTarget = -300000;  // 30mSec early

+    m_trThrottle = 0;

+    m_trRememberStampForPerf = 0;

+

+#ifdef PERF

+    m_trRememberFrameForPerf = 0;

+#endif

+

+    return NOERROR;

+} // ResetStreamingTimes

+

+

+// Reset all times controlling streaming. Note that we're now streaming. We

+// don't need to set the rendering event to have the source filter released

+// as it is done during the Run processing. When we are run we immediately

+// release the source filter thread and draw any image waiting (that image

+// may already have been drawn once as a poster frame while we were paused)

+

+HRESULT CBaseVideoRenderer::OnStartStreaming()

+{

+    ResetStreamingTimes();

+    return NOERROR;

+} // OnStartStreaming

+

+

+// Called at end of streaming.  Fixes times for property page report

+

+HRESULT CBaseVideoRenderer::OnStopStreaming()

+{

+    m_tStreamingStart = timeGetTime()-m_tStreamingStart;

+    return NOERROR;

+} // OnStopStreaming

+

+

+// Called when we start waiting for a rendering event.

+// Used to update times spent waiting and not waiting.

+

+void CBaseVideoRenderer::OnWaitStart()

+{

+    MSR_START(m_idWaitReal);

+} // OnWaitStart

+

+

+// Called when we are awoken from the wait in the window OR by our allocator

+// when it is hanging around until the next sample is due for rendering on a

+// DCI/DirectDraw surface. We add the wait time into our rolling average.

+// We grab the interface lock so that we're serialised with the application

+// thread going through the run code - which in due course ends up calling

+// ResetStreaming times - possibly as we run through this section of code

+

+void CBaseVideoRenderer::OnWaitEnd()

+{

+#ifdef PERF

+    MSR_STOP(m_idWaitReal);

+    // for a perf build we want to know just exactly how late we REALLY are.

+    // even if this means that we have to look at the clock again.

+

+    REFERENCE_TIME trRealStream;     // the real time now expressed as stream time.

+#if 0

+    m_pClock->GetTime(&trRealStream); // Calling clock here causes W95 deadlock!

+#else

+    // We will be discarding overflows like mad here!

+    // This is wrong really because timeGetTime() can wrap but it's

+    // only for PERF

+    REFERENCE_TIME tr = timeGetTime()*10000;

+    trRealStream = tr + m_llTimeOffset;

+#endif

+    trRealStream -= m_tStart;     // convert to stream time (this is a reftime)

+

+    if (m_trRememberStampForPerf==0) {

+        // This is probably the poster frame at the start, and it is not scheduled

+        // in the usual way at all.  Just count it.  The rememberstamp gets set

+        // in ShouldDrawSampleNow, so this does invalid frame recording until we

+        // actually start playing.

+        PreparePerformanceData(0, 0);

+    } else {

+        int trLate = (int)(trRealStream - m_trRememberStampForPerf);

+        int trFrame = (int)(tr - m_trRememberFrameForPerf);

+        PreparePerformanceData(trLate, trFrame);

+    }

+    m_trRememberFrameForPerf = tr;

+#endif //PERF

+} // OnWaitEnd

+

+

+// Put data on one side that describes the lateness of the current frame.

+// We don't yet know whether it will actually be drawn.  In direct draw mode,

+// this decision is up to the filter upstream, and it could change its mind.

+// The rules say that if it did draw it must call Receive().  One way or

+// another we eventually get into either OnRenderStart or OnDirectRender and

+// these both call RecordFrameLateness to update the statistics.

+

+void CBaseVideoRenderer::PreparePerformanceData(int trLate, int trFrame)

+{

+    m_trLate = trLate;

+    m_trFrame = trFrame;

+} // PreparePerformanceData

+

+

+// update the statistics:

+// m_iTotAcc, m_iSumSqAcc, m_iSumSqFrameTime, m_iSumFrameTime, m_cFramesDrawn

+// Note that because the properties page reports using these variables,

+// 1. We need to be inside a critical section

+// 2. They must all be updated together.  Updating the sums here and the count

+// elsewhere can result in imaginary jitter (i.e. attempts to find square roots

+// of negative numbers) in the property page code.

+

+void CBaseVideoRenderer::RecordFrameLateness(int trLate, int trFrame)

+{

+    // Record how timely we are.

+    int tLate = trLate/10000;

+

+    // Best estimate of moment of appearing on the screen is average of

+    // start and end draw times.  Here we have only the end time.  This may

+    // tend to show us as spuriously late by up to 1/2 frame rate achieved.

+    // Decoder probably monitors draw time.  We don't bother.

+    MSR_INTEGER( m_idFrameAccuracy, tLate );

+

+    // This is a kludge - we can get frames that are very late

+    // especially (at start-up) and they invalidate the statistics.

+    // So ignore things that are more than 1 sec off.

+    if (tLate>1000 || tLate<-1000) {

+        if (m_cFramesDrawn<=1) {

+            tLate = 0;

+        } else if (tLate>0) {

+            tLate = 1000;

+        } else {

+            tLate = -1000;

+        }

+    }

+    // The very first frame often has a invalid time, so don't

+    // count it into the statistics.   (???)

+    if (m_cFramesDrawn>1) {

+        m_iTotAcc += tLate;

+        m_iSumSqAcc += (tLate*tLate);

+    }

+

+    // calculate inter-frame time.  Doesn't make sense for first frame

+    // second frame suffers from invalid first frame stamp.

+    if (m_cFramesDrawn>2) {

+        int tFrame = trFrame/10000;    // convert to mSec else it overflows

+

+        // This is a kludge.  It can overflow anyway (a pause can cause

+        // a very long inter-frame time) and it overflows at 2**31/10**7

+        // or about 215 seconds i.e. 3min 35sec

+        if (tFrame>1000||tFrame<0) tFrame = 1000;

+        m_iSumSqFrameTime += tFrame*tFrame;

+        ASSERT(m_iSumSqFrameTime>=0);

+        m_iSumFrameTime += tFrame;

+    }

+    ++m_cFramesDrawn;

+

+} // RecordFrameLateness

+

+

+void CBaseVideoRenderer::ThrottleWait()

+{

+    if (m_trThrottle>0) {

+        int iThrottle = m_trThrottle/10000;    // convert to mSec

+        MSR_INTEGER( m_idThrottle, iThrottle);

+        DbgLog((LOG_TRACE, 0, TEXT("Throttle %d ms"), iThrottle));

+        Sleep(iThrottle);

+    } else {

+        Sleep(0);

+    }

+} // ThrottleWait

+

+

+// Whenever a frame is rendered it goes though either OnRenderStart

+// or OnDirectRender.  Data that are generated during ShouldDrawSample

+// are added to the statistics by calling RecordFrameLateness from both

+// these two places.

+

+// Called in place of OnRenderStart..OnRenderEnd

+// When a DirectDraw image is drawn

+void CBaseVideoRenderer::OnDirectRender(IMediaSample *pMediaSample)

+{

+    m_trRenderAvg = 0;

+    m_trRenderLast = 5000000;  // If we mode switch, we do NOT want this

+                               // to inhibit the new average getting going!

+                               // so we set it to half a second

+    // MSR_INTEGER(m_idRenderAvg, m_trRenderAvg/10000);

+    RecordFrameLateness(m_trLate, m_trFrame);

+    ThrottleWait();

+} // OnDirectRender

+

+

+// Called just before we start drawing.  All we do is to get the current clock

+// time (from the system) and return.  We have to store the start render time

+// in a member variable because it isn't used until we complete the drawing

+// The rest is just performance logging.

+

+void CBaseVideoRenderer::OnRenderStart(IMediaSample *pMediaSample)

+{

+    RecordFrameLateness(m_trLate, m_trFrame);

+    m_tRenderStart = timeGetTime();

+} // OnRenderStart

+

+

+// Called directly after drawing an image.  We calculate the time spent in the

+// drawing code and if this doesn't appear to have any odd looking spikes in

+// it then we add it to the current average draw time.  Measurement spikes may

+// occur if the drawing thread is interrupted and switched to somewhere else.

+

+void CBaseVideoRenderer::OnRenderEnd(IMediaSample *pMediaSample)

+{

+    // The renderer time can vary erratically if we are interrupted so we do

+    // some smoothing to help get more sensible figures out but even that is

+    // not enough as figures can go 9,10,9,9,83,9 and we must disregard 83

+

+    int tr = (timeGetTime() - m_tRenderStart)*10000;   // convert mSec->UNITS

+    if (tr < m_trRenderAvg*2 || tr < 2 * m_trRenderLast) {

+        // DO_MOVING_AVG(m_trRenderAvg, tr);

+        m_trRenderAvg = (tr + (AVGPERIOD-1)*m_trRenderAvg)/AVGPERIOD;

+    }

+    m_trRenderLast = tr;

+    ThrottleWait();

+} // OnRenderEnd

+

+

+STDMETHODIMP CBaseVideoRenderer::SetSink( IQualityControl * piqc)

+{

+

+    m_pQSink = piqc;

+

+    return NOERROR;

+} // SetSink

+

+

+STDMETHODIMP CBaseVideoRenderer::Notify( IBaseFilter * pSelf, Quality q)

+{

+    // NOTE:  We are NOT getting any locks here.  We could be called

+    // asynchronously and possibly even on a time critical thread of

+    // someone else's - so we do the minumum.  We only set one state

+    // variable (an integer) and if that happens to be in the middle

+    // of another thread reading it they will just get either the new

+    // or the old value.  Locking would achieve no more than this.

+

+    // It might be nice to check that we are being called from m_pGraph, but

+    // it turns out to be a millisecond or so per throw!

+

+    // This is heuristics, these numbers are aimed at being "what works"

+    // rather than anything based on some theory.

+    // We use a hyperbola because it's easy to calculate and it includes

+    // a panic button asymptote (which we push off just to the left)

+    // The throttling fits the following table (roughly)

+    // Proportion   Throttle (msec)

+    //     >=1000         0

+    //        900         3

+    //        800         7

+    //        700        11

+    //        600        17

+    //        500        25

+    //        400        35

+    //        300        50

+    //        200        72

+    //        125       100

+    //        100       112

+    //         50       146

+    //          0       200

+

+    // (some evidence that we could go for a sharper kink - e.g. no throttling

+    // until below the 750 mark - might give fractionally more frames on a

+    // P60-ish machine).  The easy way to get these coefficients is to use

+    // Renbase.xls follow the instructions therein using excel solver.

+

+    if (q.Proportion>=1000) { m_trThrottle = 0; }

+    else {

+        // The DWORD is to make quite sure I get unsigned arithmetic

+        // as the constant is between 2**31 and 2**32

+        m_trThrottle = -330000 + (388880000/(q.Proportion+167));

+    }

+    return NOERROR;

+} // Notify

+

+

+// Send a message to indicate what our supplier should do about quality.

+// Theory:

+// What a supplier wants to know is "is the frame I'm working on NOW

+// going to be late?".

+// F1 is the frame at the supplier (as above)

+// Tf1 is the due time for F1

+// T1 is the time at that point (NOW!)

+// Tr1 is the time that f1 WILL actually be rendered

+// L1 is the latency of the graph for frame F1 = Tr1-T1

+// D1 (for delay) is how late F1 will be beyond its due time i.e.

+// D1 = (Tr1-Tf1) which is what the supplier really wants to know.

+// Unfortunately Tr1 is in the future and is unknown, so is L1

+//

+// We could estimate L1 by its value for a previous frame,

+// L0 = Tr0-T0 and work off

+// D1' = ((T1+L0)-Tf1) = (T1 + (Tr0-T0) -Tf1)

+// Rearranging terms:

+// D1' = (T1-T0) + (Tr0-Tf1)

+//       adding (Tf0-Tf0) and rearranging again:

+//     = (T1-T0) + (Tr0-Tf0) + (Tf0-Tf1)

+//     = (T1-T0) - (Tf1-Tf0) + (Tr0-Tf0)

+// But (Tr0-Tf0) is just D0 - how late frame zero was, and this is the

+// Late field in the quality message that we send.

+// The other two terms just state what correction should be applied before

+// using the lateness of F0 to predict the lateness of F1.

+// (T1-T0) says how much time has actually passed (we have lost this much)

+// (Tf1-Tf0) says how much time should have passed if we were keeping pace

+// (we have gained this much).

+//

+// Suppliers should therefore work off:

+//    Quality.Late + (T1-T0)  - (Tf1-Tf0)

+// and see if this is "acceptably late" or even early (i.e. negative).

+// They get T1 and T0 by polling the clock, they get Tf1 and Tf0 from

+// the time stamps in the frames.  They get Quality.Late from us.

+//

+

+HRESULT CBaseVideoRenderer::SendQuality(REFERENCE_TIME trLate,

+                                        REFERENCE_TIME trRealStream)

+{

+    Quality q;

+    HRESULT hr;

+

+    // If we are the main user of time, then report this as Flood/Dry.

+    // If our suppliers are, then report it as Famine/Glut.

+    //

+    // We need to take action, but avoid hunting.  Hunting is caused by

+    // 1. Taking too much action too soon and overshooting

+    // 2. Taking too long to react (so averaging can CAUSE hunting).

+    //

+    // The reason why we use trLate as well as Wait is to reduce hunting;

+    // if the wait time is coming down and about to go into the red, we do

+    // NOT want to rely on some average which is only telling is that it used

+    // to be OK once.

+

+    q.TimeStamp = (REFERENCE_TIME)trRealStream;

+

+    if (m_trFrameAvg<0) {

+        q.Type = Famine;      // guess

+    }

+    // Is the greater part of the time taken bltting or something else

+    else if (m_trFrameAvg > 2*m_trRenderAvg) {

+        q.Type = Famine;                        // mainly other

+    } else {

+        q.Type = Flood;                         // mainly bltting

+    }

+

+    q.Proportion = 1000;               // default

+

+    if (m_trFrameAvg<0) {

+        // leave it alone - we don't know enough

+    }

+    else if ( trLate> 0 ) {

+        // try to catch up over the next second

+        // We could be Really, REALLY late, but rendering all the frames

+        // anyway, just because it's so cheap.

+

+        q.Proportion = 1000 - (int)((trLate)/(UNITS/1000));

+        if (q.Proportion<500) {

+           q.Proportion = 500;      // don't go daft. (could've been negative!)

+        } else {

+        }

+

+    } else if (  m_trWaitAvg>20000

+              && trLate<-20000

+              ){

+        // Go cautiously faster - aim at 2mSec wait.

+        if (m_trWaitAvg>=m_trFrameAvg) {

+            // This can happen because of some fudges.

+            // The waitAvg is how long we originally planned to wait

+            // The frameAvg is more honest.

+            // It means that we are spending a LOT of time waiting

+            q.Proportion = 2000;    // double.

+        } else {

+            if (m_trFrameAvg+20000 > m_trWaitAvg) {

+                q.Proportion

+                    = 1000 * (m_trFrameAvg / (m_trFrameAvg + 20000 - m_trWaitAvg));

+            } else {

+                // We're apparently spending more than the whole frame time waiting.

+                // Assume that the averages are slightly out of kilter, but that we

+                // are indeed doing a lot of waiting.  (This leg probably never

+                // happens, but the code avoids any potential divide by zero).

+                q.Proportion = 2000;

+            }

+        }

+

+        if (q.Proportion>2000) {

+            q.Proportion = 2000;    // don't go crazy.

+        }

+    }

+

+    // Tell the supplier how late frames are when they get rendered

+    // That's how late we are now.

+    // If we are in directdraw mode then the guy upstream can see the drawing

+    // times and we'll just report on the start time.  He can figure out any

+    // offset to apply.  If we are in DIB Section mode then we will apply an

+    // extra offset which is half of our drawing time.  This is usually small

+    // but can sometimes be the dominant effect.  For this we will use the

+    // average drawing time rather than the last frame.  If the last frame took

+    // a long time to draw and made us late, that's already in the lateness

+    // figure.  We should not add it in again unless we expect the next frame

+    // to be the same.  We don't, we expect the average to be a better shot.

+    // In direct draw mode the RenderAvg will be zero.

+

+    q.Late = trLate + m_trRenderAvg/2;

+

+    // log what we're doing

+    MSR_INTEGER(m_idQualityRate, q.Proportion);

+    MSR_INTEGER( m_idQualityTime, (int)q.Late / 10000 );

+

+    // A specific sink interface may be set through IPin

+

+    if (m_pQSink==NULL) {

+        // Get our input pin's peer.  We send quality management messages

+        // to any nominated receiver of these things (set in the IPin

+        // interface), or else to our source filter.

+

+        IQualityControl *pQC = NULL;

+        IPin *pOutputPin = m_pInputPin->GetConnected();

+        ASSERT(pOutputPin != NULL);

+

+        // And get an AddRef'd quality control interface

+

+        hr = pOutputPin->QueryInterface(IID_IQualityControl,(void**) &pQC);

+        if (SUCCEEDED(hr)) {

+            m_pQSink = pQC;

+        }

+    }

+    if (m_pQSink) {

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

+    }

+

+    return S_FALSE;

+

+} // SendQuality

+

+

+// We are called with a valid IMediaSample image to decide whether this is to

+// be drawn or not.  There must be a reference clock in operation.

+// Return S_OK if it is to be drawn Now (as soon as possible)

+// Return S_FALSE if it is to be drawn when it's due

+// Return an error if we want to drop it

+// m_nNormal=-1 indicates that we dropped the previous frame and so this

+// one should be drawn early.  Respect it and update it.

+// Use current stream time plus a number of heuristics (detailed below)

+// to make the decision

+

+HRESULT CBaseVideoRenderer::ShouldDrawSampleNow(IMediaSample *pMediaSample,

+                                                __inout REFERENCE_TIME *ptrStart,

+                                                __inout REFERENCE_TIME *ptrEnd)

+{

+

+    // Don't call us unless there's a clock interface to synchronise with

+    ASSERT(m_pClock);

+

+    MSR_INTEGER(m_idTimeStamp, (int)((*ptrStart)>>32));   // high order 32 bits

+    MSR_INTEGER(m_idTimeStamp, (int)(*ptrStart));         // low order 32 bits

+

+    // We lose a bit of time depending on the monitor type waiting for the next

+    // screen refresh.  On average this might be about 8mSec - so it will be

+    // later than we think when the picture appears.  To compensate a bit

+    // we bias the media samples by -8mSec i.e. 80000 UNITs.

+    // We don't ever make a stream time negative (call it paranoia)

+    if (*ptrStart>=80000) {

+        *ptrStart -= 80000;

+        *ptrEnd -= 80000;       // bias stop to to retain valid frame duration

+    }

+

+    // Cache the time stamp now.  We will want to compare what we did with what

+    // we started with (after making the monitor allowance).

+    m_trRememberStampForPerf = *ptrStart;

+

+    // Get reference times (current and late)

+    REFERENCE_TIME trRealStream;     // the real time now expressed as stream time.

+    m_pClock->GetTime(&trRealStream);

+#ifdef PERF

+    // While the reference clock is expensive:

+    // Remember the offset from timeGetTime and use that.

+    // This overflows all over the place, but when we subtract to get

+    // differences the overflows all cancel out.

+    m_llTimeOffset = trRealStream-timeGetTime()*10000;

+#endif

+    trRealStream -= m_tStart;     // convert to stream time (this is a reftime)

+

+    // We have to wory about two versions of "lateness".  The truth, which we

+    // try to work out here and the one measured against m_trTarget which

+    // includes long term feedback.  We report statistics against the truth

+    // but for operational decisions we work to the target.

+    // We use TimeDiff to make sure we get an integer because we

+    // may actually be late (or more likely early if there is a big time

+    // gap) by a very long time.

+    const int trTrueLate = TimeDiff(trRealStream - *ptrStart);

+    const int trLate = trTrueLate;

+

+    MSR_INTEGER(m_idSchLateTime, trTrueLate/10000);

+

+    // Send quality control messages upstream, measured against target

+    HRESULT hr = SendQuality(trLate, trRealStream);

+    // Note: the filter upstream is allowed to this FAIL meaning "you do it".

+    m_bSupplierHandlingQuality = (hr==S_OK);

+

+    // Decision time!  Do we drop, draw when ready or draw immediately?

+

+    const int trDuration = (int)(*ptrEnd - *ptrStart);

+    {

+        // We need to see if the frame rate of the file has just changed.

+        // This would make comparing our previous frame rate with the current

+        // frame rate inefficent.  Hang on a moment though.  I've seen files

+        // where the frames vary between 33 and 34 mSec so as to average

+        // 30fps.  A minor variation like that won't hurt us.

+        int t = m_trDuration/32;

+        if (  trDuration > m_trDuration+t

+           || trDuration < m_trDuration-t

+           ) {

+            // There's a major variation.  Reset the average frame rate to

+            // exactly the current rate to disable decision 9002 for this frame,

+            // and remember the new rate.

+            m_trFrameAvg = trDuration;

+            m_trDuration = trDuration;

+        }

+    }

+

+    MSR_INTEGER(m_idEarliness, m_trEarliness/10000);

+    MSR_INTEGER(m_idRenderAvg, m_trRenderAvg/10000);

+    MSR_INTEGER(m_idFrameAvg, m_trFrameAvg/10000);

+    MSR_INTEGER(m_idWaitAvg, m_trWaitAvg/10000);

+    MSR_INTEGER(m_idDuration, trDuration/10000);

+

+#ifdef PERF

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

+        MSR_INTEGER(m_idDecision, 9000);

+    }

+#endif

+

+    // Control the graceful slide back from slow to fast machine mode.

+    // After a frame drop accept an early frame and set the earliness to here

+    // If this frame is already later than the earliness then slide it to here

+    // otherwise do the standard slide (reduce by about 12% per frame).

+    // Note: earliness is normally NEGATIVE

+    BOOL bJustDroppedFrame

+        = (  m_bSupplierHandlingQuality

+          //  Can't use the pin sample properties because we might

+          //  not be in Receive when we call this

+          && (S_OK == pMediaSample->IsDiscontinuity())          // he just dropped one

+          )

+       || (m_nNormal==-1);                          // we just dropped one

+

+

+    // Set m_trEarliness (slide back from slow to fast machine mode)

+    if (trLate>0) {

+        m_trEarliness = 0;   // we are no longer in fast machine mode at all!

+    } else if (  (trLate>=m_trEarliness) || bJustDroppedFrame) {

+        m_trEarliness = trLate;  // Things have slipped of their own accord

+    } else {

+        m_trEarliness = m_trEarliness - m_trEarliness/8;  // graceful slide

+    }

+

+    // prepare the new wait average - but don't pollute the old one until

+    // we have finished with it.

+    int trWaitAvg;

+    {

+        // We never mix in a negative wait.  This causes us to believe in fast machines

+        // slightly more.

+        int trL = trLate<0 ? -trLate : 0;

+        trWaitAvg = (trL + m_trWaitAvg*(AVGPERIOD-1))/AVGPERIOD;

+    }

+

+

+    int trFrame;

+    {

+        REFERENCE_TIME tr = trRealStream - m_trLastDraw; // Cd be large - 4 min pause!

+        if (tr>10000000) {

+            tr = 10000000;   // 1 second - arbitrarily.

+        }

+        trFrame = int(tr);

+    }

+

+    // We will DRAW this frame IF...

+    if (

+          // ...the time we are spending drawing is a small fraction of the total

+          // observed inter-frame time so that dropping it won't help much.

+          (3*m_trRenderAvg <= m_trFrameAvg)

+

+         // ...or our supplier is NOT handling things and the next frame would

+         // be less timely than this one or our supplier CLAIMS to be handling

+         // things, and is now less than a full FOUR frames late.

+       || ( m_bSupplierHandlingQuality

+          ? (trLate <= trDuration*4)

+          : (trLate+trLate < trDuration)

+          )

+

+          // ...or we are on average waiting for over eight milliseconds then

+          // this may be just a glitch.  Draw it and we'll hope to catch up.

+       || (m_trWaitAvg > 80000)

+

+          // ...or we haven't drawn an image for over a second.  We will update

+          // the display, which stops the video looking hung.

+          // Do this regardless of how late this media sample is.

+       || ((trRealStream - m_trLastDraw) > UNITS)

+

+    ) {

+        HRESULT Result;

+

+        // We are going to play this frame.  We may want to play it early.

+        // We will play it early if we think we are in slow machine mode.

+        // If we think we are NOT in slow machine mode, we will still play

+        // it early by m_trEarliness as this controls the graceful slide back.

+        // and in addition we aim at being m_trTarget late rather than "on time".

+

+        BOOL bPlayASAP = FALSE;

+

+        // we will play it AT ONCE (slow machine mode) if...

+

+            // ...we are playing catch-up

+        if ( bJustDroppedFrame) {

+            bPlayASAP = TRUE;

+            MSR_INTEGER(m_idDecision, 9001);

+        }

+

+            // ...or if we are running below the true frame rate

+            // exact comparisons are glitchy, for these measurements,

+            // so add an extra 5% or so

+        else if (  (m_trFrameAvg > trDuration + trDuration/16)

+

+                   // It's possible to get into a state where we are losing ground, but

+                   // are a very long way ahead.  To avoid this or recover from it

+                   // we refuse to play early by more than 10 frames.

+                && (trLate > - trDuration*10)

+                ){

+            bPlayASAP = TRUE;

+            MSR_INTEGER(m_idDecision, 9002);

+        }

+#if 0

+            // ...or if we have been late and are less than one frame early

+        else if (  (trLate + trDuration > 0)

+                && (m_trWaitAvg<=20000)

+                ) {

+            bPlayASAP = TRUE;

+            MSR_INTEGER(m_idDecision, 9003);

+        }

+#endif

+        // We will NOT play it at once if we are grossly early.  On very slow frame

+        // rate movies - e.g. clock.avi - it is not a good idea to leap ahead just

+        // because we got starved (for instance by the net) and dropped one frame

+        // some time or other.  If we are more than 900mSec early, then wait.

+        if (trLate<-9000000) {

+            bPlayASAP = FALSE;

+        }

+

+        if (bPlayASAP) {

+

+            m_nNormal = 0;

+            MSR_INTEGER(m_idDecision, 0);

+            // When we are here, we are in slow-machine mode.  trLate may well

+            // oscillate between negative and positive when the supplier is

+            // dropping frames to keep sync.  We should not let that mislead

+            // us into thinking that we have as much as zero spare time!

+            // We just update with a zero wait.

+            m_trWaitAvg = (m_trWaitAvg*(AVGPERIOD-1))/AVGPERIOD;

+

+            // Assume that we draw it immediately.  Update inter-frame stats

+            m_trFrameAvg = (trFrame + m_trFrameAvg*(AVGPERIOD-1))/AVGPERIOD;

+#ifndef PERF

+            // If this is NOT a perf build, then report what we know so far

+            // without looking at the clock any more.  This assumes that we

+            // actually wait for exactly the time we hope to.  It also reports

+            // how close we get to the manipulated time stamps that we now have

+            // rather than the ones we originally started with.  It will

+            // therefore be a little optimistic.  However it's fast.

+            PreparePerformanceData(trTrueLate, trFrame);

+#endif

+            m_trLastDraw = trRealStream;

+            if (m_trEarliness > trLate) {

+                m_trEarliness = trLate;  // if we are actually early, this is neg

+            }

+            Result = S_OK;                   // Draw it now

+

+        } else {

+            ++m_nNormal;

+            // Set the average frame rate to EXACTLY the ideal rate.

+            // If we are exiting slow-machine mode then we will have caught up

+            // and be running ahead, so as we slide back to exact timing we will

+            // have a longer than usual gap at this point.  If we record this

+            // real gap then we'll think that we're running slow and go back

+            // into slow-machine mode and vever get it straight.

+            m_trFrameAvg = trDuration;

+            MSR_INTEGER(m_idDecision, 1);

+

+            // Play it early by m_trEarliness and by m_trTarget

+

+            {

+                int trE = m_trEarliness;

+                if (trE < -m_trFrameAvg) {

+                    trE = -m_trFrameAvg;

+                }

+                *ptrStart += trE;           // N.B. earliness is negative

+            }

+

+            int Delay = -trTrueLate;

+            Result = Delay<=0 ? S_OK : S_FALSE;     // OK = draw now, FALSE = wait

+

+            m_trWaitAvg = trWaitAvg;

+

+            // Predict when it will actually be drawn and update frame stats

+

+            if (Result==S_FALSE) {   // We are going to wait

+                trFrame = TimeDiff(*ptrStart-m_trLastDraw);

+                m_trLastDraw = *ptrStart;

+            } else {

+                // trFrame is already = trRealStream-m_trLastDraw;

+                m_trLastDraw = trRealStream;

+            }

+#ifndef PERF

+            int iAccuracy;

+            if (Delay>0) {

+                // Report lateness based on when we intend to play it

+                iAccuracy = TimeDiff(*ptrStart-m_trRememberStampForPerf);

+            } else {

+                // Report lateness based on playing it *now*.

+                iAccuracy = trTrueLate;     // trRealStream-RememberStampForPerf;

+            }

+            PreparePerformanceData(iAccuracy, trFrame);

+#endif

+        }

+        return Result;

+    }

+

+    // We are going to drop this frame!

+    // Of course in DirectDraw mode the guy upstream may draw it anyway.

+

+    // This will probably give a large negative wack to the wait avg.

+    m_trWaitAvg = trWaitAvg;

+

+#ifdef PERF

+    // Respect registry setting - debug only!

+    if (m_bDrawLateFrames) {

+       return S_OK;                        // draw it when it's ready

+    }                                      // even though it's late.

+#endif

+

+    // We are going to drop this frame so draw the next one early

+    // n.b. if the supplier is doing direct draw then he may draw it anyway

+    // but he's doing something funny to arrive here in that case.

+

+    MSR_INTEGER(m_idDecision, 2);

+    m_nNormal = -1;

+    return E_FAIL;                         // drop it

+

+} // ShouldDrawSampleNow

+

+

+// NOTE we're called by both the window thread and the source filter thread

+// so we have to be protected by a critical section (locked before called)

+// Also, when the window thread gets signalled to render an image, it always

+// does so regardless of how late it is. All the degradation is done when we

+// are scheduling the next sample to be drawn. Hence when we start an advise

+// link to draw a sample, that sample's time will always become the last one

+// drawn - unless of course we stop streaming in which case we cancel links

+

+BOOL CBaseVideoRenderer::ScheduleSample(IMediaSample *pMediaSample)

+{

+    // We override ShouldDrawSampleNow to add quality management

+

+    BOOL bDrawImage = CBaseRenderer::ScheduleSample(pMediaSample);

+    if (bDrawImage == FALSE) {

+	++m_cFramesDropped;

+	return FALSE;

+    }

+

+    // m_cFramesDrawn must NOT be updated here.  It has to be updated

+    // in RecordFrameLateness at the same time as the other statistics.

+    return TRUE;

+}

+

+

+// Implementation of IQualProp interface needed to support the property page

+// This is how the property page gets the data out of the scheduler. We are

+// passed into the constructor the owning object in the COM sense, this will

+// either be the video renderer or an external IUnknown if we're aggregated.

+// We initialise our CUnknown base class with this interface pointer. Then

+// all we have to do is to override NonDelegatingQueryInterface to expose

+// our IQualProp interface. The AddRef and Release are handled automatically

+// by the base class and will be passed on to the appropriate outer object

+

+STDMETHODIMP CBaseVideoRenderer::get_FramesDroppedInRenderer(__out int *pcFramesDropped)

+{

+    CheckPointer(pcFramesDropped,E_POINTER);

+    CAutoLock cVideoLock(&m_InterfaceLock);

+    *pcFramesDropped = m_cFramesDropped;

+    return NOERROR;

+} // get_FramesDroppedInRenderer

+

+

+// Set *pcFramesDrawn to the number of frames drawn since

+// streaming started.

+

+STDMETHODIMP CBaseVideoRenderer::get_FramesDrawn( int *pcFramesDrawn)

+{

+    CheckPointer(pcFramesDrawn,E_POINTER);

+    CAutoLock cVideoLock(&m_InterfaceLock);

+    *pcFramesDrawn = m_cFramesDrawn;

+    return NOERROR;

+} // get_FramesDrawn

+

+

+// Set iAvgFrameRate to the frames per hundred secs since

+// streaming started.  0 otherwise.

+

+STDMETHODIMP CBaseVideoRenderer::get_AvgFrameRate( int *piAvgFrameRate)

+{

+    CheckPointer(piAvgFrameRate,E_POINTER);

+    CAutoLock cVideoLock(&m_InterfaceLock);

+

+    int t;

+    if (m_bStreaming) {

+        t = timeGetTime()-m_tStreamingStart;

+    } else {

+        t = m_tStreamingStart;

+    }

+

+    if (t<=0) {

+        *piAvgFrameRate = 0;

+        ASSERT(m_cFramesDrawn == 0);

+    } else {

+        // i is frames per hundred seconds

+        *piAvgFrameRate = MulDiv(100000, m_cFramesDrawn, t);

+    }

+    return NOERROR;

+} // get_AvgFrameRate

+

+

+// Set *piAvg to the average sync offset since streaming started

+// in mSec.  The sync offset is the time in mSec between when the frame

+// should have been drawn and when the frame was actually drawn.

+

+STDMETHODIMP CBaseVideoRenderer::get_AvgSyncOffset(__out int *piAvg)

+{

+    CheckPointer(piAvg,E_POINTER);

+    CAutoLock cVideoLock(&m_InterfaceLock);

+

+    if (NULL==m_pClock) {

+        *piAvg = 0;

+        return NOERROR;

+    }

+

+    // Note that we didn't gather the stats on the first frame

+    // so we use m_cFramesDrawn-1 here

+    if (m_cFramesDrawn<=1) {

+        *piAvg = 0;

+    } else {

+        *piAvg = (int)(m_iTotAcc / (m_cFramesDrawn-1));

+    }

+    return NOERROR;

+} // get_AvgSyncOffset

+

+

+// To avoid dragging in the maths library - a cheap

+// approximate integer square root.

+// We do this by getting a starting guess which is between 1

+// and 2 times too large, followed by THREE iterations of

+// Newton Raphson.  (That will give accuracy to the nearest mSec

+// for the range in question - roughly 0..1000)

+//

+// It would be faster to use a linear interpolation and ONE NR, but

+// who cares.  If anyone does - the best linear interpolation is

+// to approximates sqrt(x) by

+// y = x * (sqrt(2)-1) + 1 - 1/sqrt(2) + 1/(8*(sqrt(2)-1))

+// 0r y = x*0.41421 + 0.59467

+// This minimises the maximal error in the range in question.

+// (error is about +0.008883 and then one NR will give error .0000something

+// (Of course these are integers, so you can't just multiply by 0.41421

+// you'd have to do some sort of MulDiv).

+// Anyone wanna check my maths?  (This is only for a property display!)

+

+int isqrt(int x)

+{

+    int s = 1;

+    // Make s an initial guess for sqrt(x)

+    if (x > 0x40000000) {

+       s = 0x8000;     // prevent any conceivable closed loop

+    } else {

+        while (s*s<x) {    // loop cannot possible go more than 31 times

+            s = 2*s;       // normally it goes about 6 times

+        }

+        // Three NR iterations.

+        if (x==0) {

+           s= 0; // Wouldn't it be tragic to divide by zero whenever our

+                 // accuracy was perfect!

+        } else {

+            s = (s*s+x)/(2*s);

+            if (s>=0) s = (s*s+x)/(2*s);

+            if (s>=0) s = (s*s+x)/(2*s);

+        }

+    }

+    return s;

+}

+

+//

+//  Do estimates for standard deviations for per-frame

+//  statistics

+//

+HRESULT CBaseVideoRenderer::GetStdDev(

+    int nSamples,

+    __out int *piResult,

+    LONGLONG llSumSq,

+    LONGLONG iTot

+)

+{

+    CheckPointer(piResult,E_POINTER);

+    CAutoLock cVideoLock(&m_InterfaceLock);

+

+    if (NULL==m_pClock) {

+        *piResult = 0;

+        return NOERROR;

+    }

+

+    // If S is the Sum of the Squares of observations and

+    //    T the Total (i.e. sum) of the observations and there were

+    //    N observations, then an estimate of the standard deviation is

+    //      sqrt( (S - T**2/N) / (N-1) )

+

+    if (nSamples<=1) {

+        *piResult = 0;

+    } else {

+        LONGLONG x;

+        // First frames have invalid stamps, so we get no stats for them

+        // So we need 2 frames to get 1 datum, so N is cFramesDrawn-1

+

+        // so we use m_cFramesDrawn-1 here

+        x = llSumSq - llMulDiv(iTot, iTot, nSamples, 0);

+        x = x / (nSamples-1);

+        ASSERT(x>=0);

+        *piResult = isqrt((LONG)x);

+    }

+    return NOERROR;

+}

+

+// Set *piDev to the standard deviation in mSec of the sync offset

+// of each frame since streaming started.

+

+STDMETHODIMP CBaseVideoRenderer::get_DevSyncOffset(__out int *piDev)

+{

+    // First frames have invalid stamps, so we get no stats for them

+    // So we need 2 frames to get 1 datum, so N is cFramesDrawn-1

+    return GetStdDev(m_cFramesDrawn - 1,

+                     piDev,

+                     m_iSumSqAcc,

+                     m_iTotAcc);

+} // get_DevSyncOffset

+

+

+// Set *piJitter to the standard deviation in mSec of the inter-frame time

+// of frames since streaming started.

+

+STDMETHODIMP CBaseVideoRenderer::get_Jitter(__out int *piJitter)

+{

+    // First frames have invalid stamps, so we get no stats for them

+    // So second frame gives invalid inter-frame time

+    // So we need 3 frames to get 1 datum, so N is cFramesDrawn-2

+    return GetStdDev(m_cFramesDrawn - 2,

+                     piJitter,

+                     m_iSumSqFrameTime,

+                     m_iSumFrameTime);

+} // get_Jitter

+

+

+// Overidden to return our IQualProp interface

+

+STDMETHODIMP

+CBaseVideoRenderer::NonDelegatingQueryInterface(REFIID riid,__deref_out VOID **ppv)

+{

+    // We return IQualProp and delegate everything else

+

+    if (riid == IID_IQualProp) {

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

+    } else if (riid == IID_IQualityControl) {

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

+    }

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

+}

+

+

+// Override JoinFilterGraph so that, just before leaving

+// the graph we can send an EC_WINDOW_DESTROYED event

+

+STDMETHODIMP

+CBaseVideoRenderer::JoinFilterGraph(__inout_opt IFilterGraph *pGraph, __in_opt LPCWSTR pName)

+{

+    // Since we send EC_ACTIVATE, we also need to ensure

+    // we send EC_WINDOW_DESTROYED or the resource manager may be

+    // holding us as a focus object

+    if (!pGraph && m_pGraph) {

+

+        // We were in a graph and now we're not

+        // Do this properly in case we are aggregated

+        IBaseFilter* pFilter = this;

+        NotifyEvent(EC_WINDOW_DESTROYED, (LPARAM) pFilter, 0);

+    }

+    return CBaseFilter::JoinFilterGraph(pGraph, pName);

+}

+

+

+// This removes a large number of level 4 warnings from the

+// Microsoft compiler which in this case are not very useful

+#pragma warning(disable: 4514)

+

+#endif /* PJMEDIA_VIDEO_DEV_HAS_DSHOW */

diff --git a/jni/pjproject-android/third_party/BaseClasses/renbase.h b/jni/pjproject-android/third_party/BaseClasses/renbase.h
new file mode 100644
index 0000000..c2685bb
--- /dev/null
+++ b/jni/pjproject-android/third_party/BaseClasses/renbase.h
@@ -0,0 +1,478 @@
+//------------------------------------------------------------------------------

+// File: RenBase.h

+//

+// Desc: DirectShow base classes - defines a generic ActiveX base renderer

+//       class.

+//

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

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

+

+

+#ifndef __RENBASE__

+#define __RENBASE__

+

+// Forward class declarations

+

+class CBaseRenderer;

+class CBaseVideoRenderer;

+class CRendererInputPin;

+

+// This is our input pin class that channels calls to the renderer

+

+class CRendererInputPin : public CBaseInputPin

+{

+protected:

+

+    CBaseRenderer *m_pRenderer;

+

+public:

+

+    CRendererInputPin(__inout CBaseRenderer *pRenderer,

+                      __inout HRESULT *phr,

+                      __in_opt LPCWSTR Name);

+

+    // Overriden from the base pin classes

+

+    HRESULT BreakConnect();

+    HRESULT CompleteConnect(IPin *pReceivePin);

+    HRESULT SetMediaType(const CMediaType *pmt);

+    HRESULT CheckMediaType(const CMediaType *pmt);

+    HRESULT Active();

+    HRESULT Inactive();

+

+    // Add rendering behaviour to interface functions

+

+    STDMETHODIMP QueryId(__deref_out LPWSTR *Id);

+    STDMETHODIMP EndOfStream();

+    STDMETHODIMP BeginFlush();

+    STDMETHODIMP EndFlush();

+    STDMETHODIMP Receive(IMediaSample *pMediaSample);

+

+    // Helper

+    IMemAllocator inline *Allocator() const

+    {

+        return m_pAllocator;

+    }

+};

+

+// Main renderer class that handles synchronisation and state changes

+

+class CBaseRenderer : public CBaseFilter

+{

+protected:

+

+    friend class CRendererInputPin;

+

+    friend void CALLBACK EndOfStreamTimer(UINT uID,      // Timer identifier

+                                          UINT uMsg,     // Not currently used

+                                          DWORD_PTR dwUser,  // User information

+                                          DWORD_PTR dw1,     // Windows reserved

+                                          DWORD_PTR dw2);    // Is also reserved

+

+    CRendererPosPassThru *m_pPosition;  // Media seeking pass by object

+    CAMEvent m_RenderEvent;             // Used to signal timer events

+    CAMEvent m_ThreadSignal;            // Signalled to release worker thread

+    CAMEvent m_evComplete;              // Signalled when state complete

+    BOOL m_bAbort;                      // Stop us from rendering more data

+    BOOL m_bStreaming;                  // Are we currently streaming

+    DWORD_PTR m_dwAdvise;                   // Timer advise cookie

+    IMediaSample *m_pMediaSample;       // Current image media sample

+    BOOL m_bEOS;                        // Any more samples in the stream

+    BOOL m_bEOSDelivered;               // Have we delivered an EC_COMPLETE

+    CRendererInputPin *m_pInputPin;     // Our renderer input pin object

+    CCritSec m_InterfaceLock;           // Critical section for interfaces

+    CCritSec m_RendererLock;            // Controls access to internals

+    IQualityControl * m_pQSink;         // QualityControl sink

+    BOOL m_bRepaintStatus;              // Can we signal an EC_REPAINT

+    //  Avoid some deadlocks by tracking filter during stop

+    volatile BOOL  m_bInReceive;        // Inside Receive between PrepareReceive

+                                        // And actually processing the sample

+    REFERENCE_TIME m_SignalTime;        // Time when we signal EC_COMPLETE

+    UINT m_EndOfStreamTimer;            // Used to signal end of stream

+    CCritSec m_ObjectCreationLock;      // This lock protects the creation and

+                                        // of m_pPosition and m_pInputPin.  It

+                                        // ensures that two threads cannot create

+                                        // either object simultaneously.

+

+public:

+

+    CBaseRenderer(REFCLSID RenderClass, // CLSID for this renderer

+                  __in_opt LPCTSTR pName,         // Debug ONLY description

+                  __inout_opt LPUNKNOWN pUnk,       // Aggregated owner object

+                  __inout HRESULT *phr);        // General OLE return code

+

+    ~CBaseRenderer();

+

+    // Overriden to say what interfaces we support and where

+

+    virtual HRESULT GetMediaPositionInterface(REFIID riid, __deref_out void **ppv);

+    STDMETHODIMP NonDelegatingQueryInterface(REFIID, __deref_out void **);

+

+    virtual HRESULT SourceThreadCanWait(BOOL bCanWait);

+

+#ifdef DEBUG

+    // Debug only dump of the renderer state

+    void DisplayRendererState();

+#endif

+    virtual HRESULT WaitForRenderTime();

+    virtual HRESULT CompleteStateChange(FILTER_STATE OldState);

+

+    // Return internal information about this filter

+

+    BOOL IsEndOfStream() { return m_bEOS; };

+    BOOL IsEndOfStreamDelivered() { return m_bEOSDelivered; };

+    BOOL IsStreaming() { return m_bStreaming; };

+    void SetAbortSignal(BOOL bAbort) { m_bAbort = bAbort; };

+    virtual void OnReceiveFirstSample(IMediaSample *pMediaSample) { };

+    CAMEvent *GetRenderEvent() { return &m_RenderEvent; };

+

+    // Permit access to the transition state

+

+    void Ready() { m_evComplete.Set(); };

+    void NotReady() { m_evComplete.Reset(); };

+    BOOL CheckReady() { return m_evComplete.Check(); };

+

+    virtual int GetPinCount();

+    virtual CBasePin *GetPin(int n);

+    FILTER_STATE GetRealState();

+    void SendRepaint();

+    void SendNotifyWindow(IPin *pPin,HWND hwnd);

+    BOOL OnDisplayChange();

+    void SetRepaintStatus(BOOL bRepaint);

+

+    // Override the filter and pin interface functions

+

+    STDMETHODIMP Stop();

+    STDMETHODIMP Pause();

+    STDMETHODIMP Run(REFERENCE_TIME StartTime);

+    STDMETHODIMP GetState(DWORD dwMSecs, __out FILTER_STATE *State);

+    STDMETHODIMP FindPin(LPCWSTR Id, __deref_out IPin **ppPin);

+

+    // These are available for a quality management implementation

+

+    virtual void OnRenderStart(IMediaSample *pMediaSample);

+    virtual void OnRenderEnd(IMediaSample *pMediaSample);

+    virtual HRESULT OnStartStreaming() { return NOERROR; };

+    virtual HRESULT OnStopStreaming() { return NOERROR; };

+    virtual void OnWaitStart() { };

+    virtual void OnWaitEnd() { };

+    virtual void PrepareRender() { };

+

+#ifdef PERF

+    REFERENCE_TIME m_trRenderStart; // Just before we started drawing

+                                    // Set in OnRenderStart, Used in OnRenderEnd

+    int m_idBaseStamp;              // MSR_id for frame time stamp

+    int m_idBaseRenderTime;         // MSR_id for true wait time

+    int m_idBaseAccuracy;           // MSR_id for time frame is late (int)

+#endif

+

+    // Quality management implementation for scheduling rendering

+

+    virtual BOOL ScheduleSample(IMediaSample *pMediaSample);

+    virtual HRESULT GetSampleTimes(IMediaSample *pMediaSample,

+                                   __out REFERENCE_TIME *pStartTime,

+                                   __out REFERENCE_TIME *pEndTime);

+

+    virtual HRESULT ShouldDrawSampleNow(IMediaSample *pMediaSample,

+                                        __out REFERENCE_TIME *ptrStart,

+                                        __out REFERENCE_TIME *ptrEnd);

+

+    // Lots of end of stream complexities

+

+    void TimerCallback();

+    void ResetEndOfStreamTimer();

+    HRESULT NotifyEndOfStream();

+    virtual HRESULT SendEndOfStream();

+    virtual HRESULT ResetEndOfStream();

+    virtual HRESULT EndOfStream();

+

+    // Rendering is based around the clock

+

+    void SignalTimerFired();

+    virtual HRESULT CancelNotification();

+    virtual HRESULT ClearPendingSample();

+

+    // Called when the filter changes state

+

+    virtual HRESULT Active();

+    virtual HRESULT Inactive();

+    virtual HRESULT StartStreaming();

+    virtual HRESULT StopStreaming();

+    virtual HRESULT BeginFlush();

+    virtual HRESULT EndFlush();

+

+    // Deal with connections and type changes

+

+    virtual HRESULT BreakConnect();

+    virtual HRESULT SetMediaType(const CMediaType *pmt);

+    virtual HRESULT CompleteConnect(IPin *pReceivePin);

+

+    // These look after the handling of data samples

+

+    virtual HRESULT PrepareReceive(IMediaSample *pMediaSample);

+    virtual HRESULT Receive(IMediaSample *pMediaSample);

+    virtual BOOL HaveCurrentSample();

+    virtual IMediaSample *GetCurrentSample();

+    virtual HRESULT Render(IMediaSample *pMediaSample);

+

+    // Derived classes MUST override these

+    virtual HRESULT DoRenderSample(IMediaSample *pMediaSample) PURE;

+    virtual HRESULT CheckMediaType(const CMediaType *) PURE;

+

+    // Helper

+    void WaitForReceiveToComplete();

+};

+

+

+// CBaseVideoRenderer is a renderer class (see its ancestor class) and

+// it handles scheduling of media samples so that they are drawn at the

+// correct time by the reference clock.  It implements a degradation

+// strategy.  Possible degradation modes are:

+//    Drop frames here (only useful if the drawing takes significant time)

+//    Signal supplier (upstream) to drop some frame(s) - i.e. one-off skip.

+//    Signal supplier to change the frame rate - i.e. ongoing skipping.

+//    Or any combination of the above.

+// In order to determine what's useful to try we need to know what's going

+// on.  This is done by timing various operations (including the supplier).

+// This timing is done by using timeGetTime as it is accurate enough and

+// usually cheaper than calling the reference clock.  It also tells the

+// truth if there is an audio break and the reference clock stops.

+// We provide a number of public entry points (named OnXxxStart, OnXxxEnd)

+// which the rest of the renderer calls at significant moments.  These do

+// the timing.

+

+// the number of frames that the sliding averages are averaged over.

+// the rule is (1024*NewObservation + (AVGPERIOD-1) * PreviousAverage)/AVGPERIOD

+#define AVGPERIOD 4

+#define DO_MOVING_AVG(avg,obs) (avg = (1024*obs + (AVGPERIOD-1)*avg)/AVGPERIOD)

+// Spot the bug in this macro - I can't. but it doesn't work!

+

+class CBaseVideoRenderer : public CBaseRenderer,    // Base renderer class

+                           public IQualProp,        // Property page guff

+                           public IQualityControl   // Allow throttling

+{

+protected:

+

+    // Hungarian:

+    //     tFoo is the time Foo in mSec (beware m_tStart from filter.h)

+    //     trBar is the time Bar by the reference clock

+

+    //******************************************************************

+    // State variables to control synchronisation

+    //******************************************************************

+

+    // Control of sending Quality messages.  We need to know whether

+    // we are in trouble (e.g. frames being dropped) and where the time

+    // is being spent.

+

+    // When we drop a frame we play the next one early.

+    // The frame after that is likely to wait before drawing and counting this

+    // wait as spare time is unfair, so we count it as a zero wait.

+    // We therefore need to know whether we are playing frames early or not.

+

+    int m_nNormal;                  // The number of consecutive frames

+                                    // drawn at their normal time (not early)

+                                    // -1 means we just dropped a frame.

+

+#ifdef PERF

+    BOOL m_bDrawLateFrames;         // Don't drop any frames (debug and I'm

+                                    // not keen on people using it!)

+#endif

+

+    BOOL m_bSupplierHandlingQuality;// The response to Quality messages says

+                                    // our supplier is handling things.

+                                    // We will allow things to go extra late

+                                    // before dropping frames.  We will play

+                                    // very early after he has dropped one.

+

+    // Control of scheduling, frame dropping etc.

+    // We need to know where the time is being spent so as to tell whether

+    // we should be taking action here, signalling supplier or what.

+    // The variables are initialised to a mode of NOT dropping frames.

+    // They will tell the truth after a few frames.

+    // We typically record a start time for an event, later we get the time

+    // again and subtract to get the elapsed time, and we average this over

+    // a few frames.  The average is used to tell what mode we are in.

+

+    // Although these are reference times (64 bit) they are all DIFFERENCES

+    // between times which are small.  An int will go up to 214 secs before

+    // overflow.  Avoiding 64 bit multiplications and divisions seems

+    // worth while.

+

+

+

+    // Audio-video throttling.  If the user has turned up audio quality

+    // very high (in principle it could be any other stream, not just audio)

+    // then we can receive cries for help via the graph manager.  In this case

+    // we put in a wait for some time after rendering each frame.

+    int m_trThrottle;

+

+    // The time taken to render (i.e. BitBlt) frames controls which component

+    // needs to degrade.  If the blt is expensive, the renderer degrades.

+    // If the blt is cheap it's done anyway and the supplier degrades.

+    int m_trRenderAvg;              // Time frames are taking to blt

+    int m_trRenderLast;             // Time for last frame blt

+    int m_tRenderStart;             // Just before we started drawing (mSec)

+                                    // derived from timeGetTime.

+

+    // When frames are dropped we will play the next frame as early as we can.

+    // If it was a false alarm and the machine is fast we slide gently back to

+    // normal timing.  To do this, we record the offset showing just how early

+    // we really are.  This will normally be negative meaning early or zero.

+    int m_trEarliness;

+

+    // Target provides slow long-term feedback to try to reduce the

+    // average sync offset to zero.  Whenever a frame is actually rendered

+    // early we add a msec or two, whenever late we take off a few.

+    // We add or take off 1/32 of the error time.

+    // Eventually we should be hovering around zero.  For a really bad case

+    // where we were (say) 300mSec off, it might take 100 odd frames to

+    // settle down.  The rate of change of this is intended to be slower

+    // than any other mechanism in Quartz, thereby avoiding hunting.

+    int m_trTarget;

+

+    // The proportion of time spent waiting for the right moment to blt

+    // controls whether we bother to drop a frame or whether we reckon that

+    // we're doing well enough that we can stand a one-frame glitch.

+    int m_trWaitAvg;                // Average of last few wait times

+                                    // (actually we just average how early

+                                    // we were).  Negative here means LATE.

+

+    // The average inter-frame time.

+    // This is used to calculate the proportion of the time used by the

+    // three operations (supplying us, waiting, rendering)

+    int m_trFrameAvg;               // Average inter-frame time

+    int m_trDuration;               // duration of last frame.

+

+#ifdef PERF

+    // Performance logging identifiers

+    int m_idTimeStamp;              // MSR_id for frame time stamp

+    int m_idEarliness;              // MSR_id for earliness fudge

+    int m_idTarget;                 // MSR_id for Target fudge

+    int m_idWaitReal;               // MSR_id for true wait time

+    int m_idWait;                   // MSR_id for wait time recorded

+    int m_idFrameAccuracy;          // MSR_id for time frame is late (int)

+    int m_idRenderAvg;              // MSR_id for Render time recorded (int)

+    int m_idSchLateTime;            // MSR_id for lateness at scheduler

+    int m_idQualityRate;            // MSR_id for Quality rate requested

+    int m_idQualityTime;            // MSR_id for Quality time requested

+    int m_idDecision;               // MSR_id for decision code

+    int m_idDuration;               // MSR_id for duration of a frame

+    int m_idThrottle;               // MSR_id for audio-video throttling

+    //int m_idDebug;                  // MSR_id for trace style debugging

+    //int m_idSendQuality;          // MSR_id for timing the notifications per se

+#endif // PERF

+    REFERENCE_TIME m_trRememberStampForPerf;  // original time stamp of frame

+                                              // with no earliness fudges etc.

+#ifdef PERF

+    REFERENCE_TIME m_trRememberFrameForPerf;  // time when previous frame rendered

+

+    // debug...

+    int m_idFrameAvg;

+    int m_idWaitAvg;

+#endif

+

+    // PROPERTY PAGE

+    // This has edit fields that show the user what's happening

+    // These member variables hold these counts.

+

+    int m_cFramesDropped;           // cumulative frames dropped IN THE RENDERER

+    int m_cFramesDrawn;             // Frames since streaming started seen BY THE

+                                    // RENDERER (some may be dropped upstream)

+

+    // Next two support average sync offset and standard deviation of sync offset.

+    LONGLONG m_iTotAcc;                  // Sum of accuracies in mSec

+    LONGLONG m_iSumSqAcc;           // Sum of squares of (accuracies in mSec)

+

+    // Next two allow jitter calculation.  Jitter is std deviation of frame time.

+    REFERENCE_TIME m_trLastDraw;    // Time of prev frame (for inter-frame times)

+    LONGLONG m_iSumSqFrameTime;     // Sum of squares of (inter-frame time in mSec)

+    LONGLONG m_iSumFrameTime;            // Sum of inter-frame times in mSec

+

+    // To get performance statistics on frame rate, jitter etc, we need

+    // to record the lateness and inter-frame time.  What we actually need are the

+    // data above (sum, sum of squares and number of entries for each) but the data

+    // is generated just ahead of time and only later do we discover whether the

+    // frame was actually drawn or not.  So we have to hang on to the data

+    int m_trLate;                   // hold onto frame lateness

+    int m_trFrame;                  // hold onto inter-frame time

+

+    int m_tStreamingStart;          // if streaming then time streaming started

+                                    // else time of last streaming session

+                                    // used for property page statistics

+#ifdef PERF

+    LONGLONG m_llTimeOffset;        // timeGetTime()*10000+m_llTimeOffset==ref time

+#endif

+

+public:

+

+

+    CBaseVideoRenderer(REFCLSID RenderClass, // CLSID for this renderer

+                       __in_opt LPCTSTR pName,         // Debug ONLY description

+                       __inout_opt LPUNKNOWN pUnk,       // Aggregated owner object

+                       __inout HRESULT *phr);        // General OLE return code

+

+    ~CBaseVideoRenderer();

+

+    // IQualityControl methods - Notify allows audio-video throttling

+

+    STDMETHODIMP SetSink( IQualityControl * piqc);

+    STDMETHODIMP Notify( IBaseFilter * pSelf, Quality q);

+

+    // These provide a full video quality management implementation

+

+    void OnRenderStart(IMediaSample *pMediaSample);

+    void OnRenderEnd(IMediaSample *pMediaSample);

+    void OnWaitStart();

+    void OnWaitEnd();

+    HRESULT OnStartStreaming();

+    HRESULT OnStopStreaming();

+    void ThrottleWait();

+

+    // Handle the statistics gathering for our quality management

+

+    void PreparePerformanceData(int trLate, int trFrame);

+    virtual void RecordFrameLateness(int trLate, int trFrame);

+    virtual void OnDirectRender(IMediaSample *pMediaSample);

+    virtual HRESULT ResetStreamingTimes();

+    BOOL ScheduleSample(IMediaSample *pMediaSample);

+    HRESULT ShouldDrawSampleNow(IMediaSample *pMediaSample,

+                                __inout REFERENCE_TIME *ptrStart,

+                                __inout REFERENCE_TIME *ptrEnd);

+

+    virtual HRESULT SendQuality(REFERENCE_TIME trLate, REFERENCE_TIME trRealStream);

+    STDMETHODIMP JoinFilterGraph(__inout_opt IFilterGraph * pGraph, __in_opt LPCWSTR pName);

+

+    //

+    //  Do estimates for standard deviations for per-frame

+    //  statistics

+    //

+    //  *piResult = (llSumSq - iTot * iTot / m_cFramesDrawn - 1) /

+    //                            (m_cFramesDrawn - 2)

+    //  or 0 if m_cFramesDrawn <= 3

+    //

+    HRESULT GetStdDev(

+        int nSamples,

+        __out int *piResult,

+        LONGLONG llSumSq,

+        LONGLONG iTot

+    );

+public:

+

+    // IQualProp property page support

+

+    STDMETHODIMP get_FramesDroppedInRenderer(__out int *cFramesDropped);

+    STDMETHODIMP get_FramesDrawn(__out int *pcFramesDrawn);

+    STDMETHODIMP get_AvgFrameRate(__out int *piAvgFrameRate);

+    STDMETHODIMP get_Jitter(__out int *piJitter);

+    STDMETHODIMP get_AvgSyncOffset(__out int *piAvg);

+    STDMETHODIMP get_DevSyncOffset(__out int *piDev);

+

+    // Implement an IUnknown interface and expose IQualProp

+

+    DECLARE_IUNKNOWN

+    STDMETHODIMP NonDelegatingQueryInterface(REFIID riid,__deref_out VOID **ppv);

+};

+

+#endif // __RENBASE__

+

diff --git a/jni/pjproject-android/third_party/BaseClasses/seekpt.h b/jni/pjproject-android/third_party/BaseClasses/seekpt.h
new file mode 100644
index 0000000..208d418
--- /dev/null
+++ b/jni/pjproject-android/third_party/BaseClasses/seekpt.h
@@ -0,0 +1,30 @@
+//------------------------------------------------------------------------------

+// File: SeekPT.h

+//

+// Desc: DirectShow base classes.

+//

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

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

+

+

+#ifndef __seekpt_h__

+#define __seekpt_h__

+

+

+class CSeekingPassThru : public ISeekingPassThru, public CUnknown

+{

+public:

+    static CUnknown *CreateInstance(__inout_opt LPUNKNOWN pUnk, __inout HRESULT *phr);

+    CSeekingPassThru(__in_opt LPCTSTR pName, __inout_opt LPUNKNOWN pUnk, __inout HRESULT *phr);

+    ~CSeekingPassThru();

+

+    DECLARE_IUNKNOWN;

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

+

+    STDMETHODIMP Init(BOOL bSupportRendering, IPin *pPin);

+

+private:

+    CPosPassThru              *m_pPosPassThru;

+};

+

+#endif

diff --git a/jni/pjproject-android/third_party/BaseClasses/streams.h b/jni/pjproject-android/third_party/BaseClasses/streams.h
new file mode 100644
index 0000000..63f33df
--- /dev/null
+++ b/jni/pjproject-android/third_party/BaseClasses/streams.h
@@ -0,0 +1,202 @@
+//------------------------------------------------------------------------------

+// File: Streams.h

+//

+// Desc: DirectShow base classes - defines overall streams architecture.

+//

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

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

+

+

+#ifndef __STREAMS__

+#define __STREAMS__

+

+#ifdef	_MSC_VER

+// disable some level-4 warnings, use #pragma warning(enable:###) to re-enable

+#pragma warning(disable:4100) // warning C4100: unreferenced formal parameter

+#pragma warning(disable:4201) // warning C4201: nonstandard extension used : nameless struct/union

+#pragma warning(disable:4511) // warning C4511: copy constructor could not be generated

+#pragma warning(disable:4512) // warning C4512: assignment operator could not be generated

+#pragma warning(disable:4514) // warning C4514: "unreferenced inline function has been removed"

+

+#if _MSC_VER>=1100

+#define AM_NOVTABLE __declspec(novtable)

+#else

+#define AM_NOVTABLE

+#endif

+#endif	// MSC_VER

+

+

+// Because of differences between Visual C++ and older Microsoft SDKs,

+// you may have defined _DEBUG without defining DEBUG.  This logic

+// ensures that both will be set if Visual C++ sets _DEBUG.

+#ifdef _DEBUG

+#ifndef DEBUG

+#define DEBUG

+#endif

+#endif

+

+

+#include <windows.h>

+#include <windowsx.h>

+#include <olectl.h>

+#include <ddraw.h>

+#include <mmsystem.h>

+

+

+#ifndef NUMELMS

+#if _WIN32_WINNT < 0x0600

+   #define NUMELMS(aa) (sizeof(aa)/sizeof((aa)[0]))

+#else

+   #define NUMELMS(aa) ARRAYSIZE(aa)

+#endif   

+#endif

+

+///////////////////////////////////////////////////////////////////////////

+// The following definitions come from the Platform SDK and are required if

+// the applicaiton is being compiled with the headers from Visual C++ 6.0.

+/////////////////////////////////////////////////// ////////////////////////

+#ifndef InterlockedExchangePointer

+	#define InterlockedExchangePointer(Target, Value) \

+   (PVOID)InterlockedExchange((PLONG)(Target), (LONG)(Value))

+#endif

+

+#ifndef _WAVEFORMATEXTENSIBLE_

+#define _WAVEFORMATEXTENSIBLE_

+typedef struct {

+    WAVEFORMATEX    Format;

+    union {

+        WORD wValidBitsPerSample;       /* bits of precision  */

+        WORD wSamplesPerBlock;          /* valid if wBitsPerSample==0 */

+        WORD wReserved;                 /* If neither applies, set to zero. */

+    } Samples;

+    DWORD           dwChannelMask;      /* which channels are */

+                                        /* present in stream  */

+    GUID            SubFormat;

+} WAVEFORMATEXTENSIBLE, *PWAVEFORMATEXTENSIBLE;

+#endif // !_WAVEFORMATEXTENSIBLE_

+

+#if !defined(WAVE_FORMAT_EXTENSIBLE)

+#define  WAVE_FORMAT_EXTENSIBLE                 0xFFFE

+#endif // !defined(WAVE_FORMAT_EXTENSIBLE)

+

+#ifndef GetWindowLongPtr

+  #define GetWindowLongPtrA   GetWindowLongA

+  #define GetWindowLongPtrW   GetWindowLongW

+  #ifdef UNICODE

+    #define GetWindowLongPtr  GetWindowLongPtrW

+  #else

+    #define GetWindowLongPtr  GetWindowLongPtrA

+  #endif // !UNICODE

+#endif // !GetWindowLongPtr

+

+#ifndef SetWindowLongPtr

+  #define SetWindowLongPtrA   SetWindowLongA

+  #define SetWindowLongPtrW   SetWindowLongW

+  #ifdef UNICODE

+    #define SetWindowLongPtr  SetWindowLongPtrW

+  #else

+    #define SetWindowLongPtr  SetWindowLongPtrA

+  #endif // !UNICODE

+#endif // !SetWindowLongPtr

+

+#ifndef GWLP_WNDPROC

+  #define GWLP_WNDPROC        (-4)

+#endif

+#ifndef GWLP_HINSTANCE

+  #define GWLP_HINSTANCE      (-6)

+#endif

+#ifndef GWLP_HWNDPARENT

+  #define GWLP_HWNDPARENT     (-8)

+#endif

+#ifndef GWLP_USERDATA

+  #define GWLP_USERDATA       (-21)

+#endif

+#ifndef GWLP_ID

+  #define GWLP_ID             (-12)

+#endif

+#ifndef DWLP_MSGRESULT

+  #define DWLP_MSGRESULT  0

+#endif

+#ifndef DWLP_DLGPROC 

+  #define DWLP_DLGPROC    DWLP_MSGRESULT + sizeof(LRESULT)

+#endif

+#ifndef DWLP_USER

+  #define DWLP_USER       DWLP_DLGPROC + sizeof(DLGPROC)

+#endif

+

+

+#pragma warning(push)

+#pragma warning(disable: 4312 4244)

+// _GetWindowLongPtr

+// Templated version of GetWindowLongPtr, to suppress spurious compiler warning.

+template <class T>

+T _GetWindowLongPtr(HWND hwnd, int nIndex)

+{

+    return (T)GetWindowLongPtr(hwnd, nIndex);

+}

+

+// _SetWindowLongPtr

+// Templated version of SetWindowLongPtr, to suppress spurious compiler warning.

+template <class T>

+LONG_PTR _SetWindowLongPtr(HWND hwnd, int nIndex, T p)

+{

+    return SetWindowLongPtr(hwnd, nIndex, (LONG_PTR)p);

+}

+#pragma warning(pop)

+

+///////////////////////////////////////////////////////////////////////////

+// End Platform SDK definitions

+///////////////////////////////////////////////////////////////////////////

+

+

+#include <strmif.h>     // Generated IDL header file for streams interfaces

+#include <intsafe.h>    // required by amvideo.h

+

+#include <reftime.h>    // Helper class for REFERENCE_TIME management

+#include <wxdebug.h>    // Debug support for logging and ASSERTs

+#include <amvideo.h>    // ActiveMovie video interfaces and definitions

+//include amaudio.h explicitly if you need it.  it requires the DX SDK.

+//#include <amaudio.h>    // ActiveMovie audio interfaces and definitions

+#include <wxutil.h>     // General helper classes for threads etc

+#include <combase.h>    // Base COM classes to support IUnknown

+//#include <dllsetup.h>   // Filter registration support functions

+#include <measure.h>    // Performance measurement

+//#include <comlite.h>    // Light weight com function prototypes

+

+//#include <cache.h>      // Simple cache container class

+#include <wxlist.h>     // Non MFC generic list class

+#include <msgthrd.h>	// CMsgThread

+#include <mtype.h>      // Helper class for managing media types

+#include <fourcc.h>     // conversions between FOURCCs and GUIDs

+#include <control.h>    // generated from control.odl

+#include <ctlutil.h>    // control interface utility classes

+#include <evcode.h>     // event code definitions

+#include <amfilter.h>   // Main streams architecture class hierachy

+//#include <transfrm.h>   // Generic transform filter

+//#include <transip.h>    // Generic transform-in-place filter

+#include <uuids.h>      // declaration of type GUIDs and well-known clsids

+//#include <source.h>	// Generic source filter

+//#include <outputq.h>    // Output pin queueing

+#include <errors.h>     // HRESULT status and error definitions

+#include <renbase.h>    // Base class for writing ActiveX renderers

+//#include <winutil.h>    // Helps with filters that manage windows

+//#include <winctrl.h>    // Implements the IVideoWindow interface

+//#include <videoctl.h>   // Specifically video related classes

+const LONGLONG MAX_TIME = 0x7FFFFFFFFFFFFFFF;   /* Maximum LONGLONG value */

+//#include <refclock.h>	// Base clock class

+//#include <sysclock.h>	// System clock

+//#include <pstream.h>    // IPersistStream helper class

+//#include <vtrans.h>     // Video Transform Filter base class

+//#include <amextra.h>

+//#include <cprop.h>      // Base property page class

+//#include <strmctl.h>    // IAMStreamControl support

+//#include <edevdefs.h>   // External device control interface defines

+//#include <audevcod.h>   // audio filter device error event codes

+

+

+#else

+    #ifdef DEBUG

+    #pragma message("STREAMS.H included TWICE")

+    #endif

+#endif // __STREAMS__

+

diff --git a/jni/pjproject-android/third_party/BaseClasses/wxdebug.cpp b/jni/pjproject-android/third_party/BaseClasses/wxdebug.cpp
new file mode 100644
index 0000000..3a1dc38
--- /dev/null
+++ b/jni/pjproject-android/third_party/BaseClasses/wxdebug.cpp
@@ -0,0 +1,1478 @@
+//------------------------------------------------------------------------------

+// File: WXDebug.cpp

+//

+// Desc: DirectShow base classes - implements ActiveX system debugging

+//       facilities.

+//

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

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

+

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

+

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

+

+#define _WINDLL

+

+#include <streams.h>

+#include <stdarg.h>

+#include <stdio.h>

+#include <dvdmedia.h>

+

+#ifdef DEBUG

+#ifdef UNICODE

+#ifndef _UNICODE

+#define _UNICODE

+#endif // _UNICODE

+#endif // UNICODE

+#endif // DEBUG

+

+#include <tchar.h>

+#include <strsafe.h>

+

+#ifdef DEBUG

+static void DisplayBITMAPINFO(const BITMAPINFOHEADER* pbmi);

+static void DisplayRECT(LPCTSTR szLabel, const RECT& rc);

+

+// The Win32 wsprintf() function writes a maximum of 1024 characters to it's output buffer.

+// See the documentation for wsprintf()'s lpOut parameter for more information.

+const INT iDEBUGINFO = 1024;                 // Used to format strings

+

+/* For every module and executable we store a debugging level for each of

+   the five categories (eg LOG_ERROR and LOG_TIMING). This makes it easy

+   to isolate and debug individual modules without seeing everybody elses

+   spurious debug output. The keys are stored in the registry under the

+   HKEY_LOCAL_MACHINE\SOFTWARE\Debug\<Module Name>\<KeyName> key values

+   NOTE these must be in the same order as their enumeration definition */

+

+const LPCTSTR pKeyNames[] = {

+    TEXT("TIMING"),      // Timing and performance measurements

+    TEXT("TRACE"),       // General step point call tracing

+    TEXT("MEMORY"),      // Memory and object allocation/destruction

+    TEXT("LOCKING"),     // Locking/unlocking of critical sections

+    TEXT("ERROR"),       // Debug error notification

+    TEXT("CUSTOM1"),

+    TEXT("CUSTOM2"),

+    TEXT("CUSTOM3"),

+    TEXT("CUSTOM4"),

+    TEXT("CUSTOM5")

+    };

+

+const TCHAR CAutoTrace::_szEntering[] = TEXT("->: %s");

+const TCHAR CAutoTrace::_szLeaving[]  = TEXT("<-: %s");

+

+const INT iMAXLEVELS = NUMELMS(pKeyNames);  // Maximum debug categories

+

+HINSTANCE m_hInst;                          // Module instance handle

+TCHAR m_ModuleName[iDEBUGINFO];             // Cut down module name

+DWORD m_Levels[iMAXLEVELS];                 // Debug level per category

+CRITICAL_SECTION m_CSDebug;                 // Controls access to list

+DWORD m_dwNextCookie;                       // Next active object ID

+ObjectDesc *pListHead = NULL;               // First active object

+DWORD m_dwObjectCount;                      // Active object count

+BOOL m_bInit = FALSE;                       // Have we been initialised

+HANDLE m_hOutput = INVALID_HANDLE_VALUE;    // Optional output written here

+DWORD dwWaitTimeout = INFINITE;             // Default timeout value

+DWORD dwTimeOffset;			    // Time of first DbgLog call

+bool g_fUseKASSERT = false;                 // don't create messagebox

+bool g_fDbgInDllEntryPoint = false;

+bool g_fAutoRefreshLevels = false;

+

+LPCTSTR pBaseKey = TEXT("SOFTWARE\\Microsoft\\DirectShow\\Debug");

+LPCTSTR pGlobalKey = TEXT("GLOBAL");

+static CHAR *pUnknownName = "UNKNOWN";

+

+LPCTSTR TimeoutName = TEXT("TIMEOUT");

+

+/* This sets the instance handle that the debug library uses to find

+   the module's file name from the Win32 GetModuleFileName function */

+

+void WINAPI DbgInitialise(HINSTANCE hInst)

+{

+    InitializeCriticalSection(&m_CSDebug);

+    m_bInit = TRUE;

+

+    m_hInst = hInst;

+    DbgInitModuleName();

+    if (GetProfileInt(m_ModuleName, TEXT("BreakOnLoad"), 0))

+       DebugBreak();

+    DbgInitModuleSettings(false);

+    DbgInitGlobalSettings(true);

+    dwTimeOffset = timeGetTime();

+}

+

+

+/* This is called to clear up any resources the debug library uses - at the

+   moment we delete our critical section and the object list. The values we

+   retrieve from the registry are all done during initialisation but we don't

+   go looking for update notifications while we are running, if the values

+   are changed then the application has to be restarted to pick them up */

+

+void WINAPI DbgTerminate()

+{

+    if (m_hOutput != INVALID_HANDLE_VALUE) {

+       EXECUTE_ASSERT(CloseHandle(m_hOutput));

+       m_hOutput = INVALID_HANDLE_VALUE;

+    }

+    DeleteCriticalSection(&m_CSDebug);

+    m_bInit = FALSE;

+}

+

+

+/* This is called by DbgInitLogLevels to read the debug settings

+   for each logging category for this module from the registry */

+

+void WINAPI DbgInitKeyLevels(HKEY hKey, bool fTakeMax)

+{

+    LONG lReturn;               // Create key return value

+    LONG lKeyPos;               // Current key category

+    DWORD dwKeySize;            // Size of the key value

+    DWORD dwKeyType;            // Receives it's type

+    DWORD dwKeyValue;           // This fields value

+

+    /* Try and read a value for each key position in turn */

+    for (lKeyPos = 0;lKeyPos < iMAXLEVELS;lKeyPos++) {

+

+        dwKeySize = sizeof(DWORD);

+        lReturn = RegQueryValueEx(

+            hKey,                       // Handle to an open key

+            pKeyNames[lKeyPos],         // Subkey name derivation

+            NULL,                       // Reserved field

+            &dwKeyType,                 // Returns the field type

+            (LPBYTE) &dwKeyValue,       // Returns the field's value

+            &dwKeySize );               // Number of bytes transferred

+

+        /* If either the key was not available or it was not a DWORD value

+           then we ensure only the high priority debug logging is output

+           but we try and update the field to a zero filled DWORD value */

+

+        if (lReturn != ERROR_SUCCESS || dwKeyType != REG_DWORD)  {

+

+            dwKeyValue = 0;

+            lReturn = RegSetValueEx(

+                hKey,                   // Handle of an open key

+                pKeyNames[lKeyPos],     // Address of subkey name

+                (DWORD) 0,              // Reserved field

+                REG_DWORD,              // Type of the key field

+                (PBYTE) &dwKeyValue,    // Value for the field

+                sizeof(DWORD));         // Size of the field buffer

+

+            if (lReturn != ERROR_SUCCESS) {

+                DbgLog((LOG_ERROR,1,TEXT("Could not create subkey %s"),pKeyNames[lKeyPos]));

+                dwKeyValue = 0;

+            }

+        }

+        if(fTakeMax)

+        {

+            m_Levels[lKeyPos] = max(dwKeyValue,m_Levels[lKeyPos]);

+        }

+        else

+        {

+            if((m_Levels[lKeyPos] & LOG_FORCIBLY_SET) == 0) {

+                m_Levels[lKeyPos] = dwKeyValue;

+            }

+        }

+    }

+

+    /*  Read the timeout value for catching hangs */

+    dwKeySize = sizeof(DWORD);

+    lReturn = RegQueryValueEx(

+        hKey,                       // Handle to an open key

+        TimeoutName,                // Subkey name derivation

+        NULL,                       // Reserved field

+        &dwKeyType,                 // Returns the field type

+        (LPBYTE) &dwWaitTimeout,    // Returns the field's value

+        &dwKeySize );               // Number of bytes transferred

+

+    /* If either the key was not available or it was not a DWORD value

+       then we ensure only the high priority debug logging is output

+       but we try and update the field to a zero filled DWORD value */

+

+    if (lReturn != ERROR_SUCCESS || dwKeyType != REG_DWORD)  {

+

+        dwWaitTimeout = INFINITE;

+        lReturn = RegSetValueEx(

+            hKey,                   // Handle of an open key

+            TimeoutName,            // Address of subkey name

+            (DWORD) 0,              // Reserved field

+            REG_DWORD,              // Type of the key field

+            (PBYTE) &dwWaitTimeout, // Value for the field

+            sizeof(DWORD));         // Size of the field buffer

+

+        if (lReturn != ERROR_SUCCESS) {

+            DbgLog((LOG_ERROR,1,TEXT("Could not create subkey %s"),pKeyNames[lKeyPos]));

+            dwWaitTimeout = INFINITE;

+        }

+    }

+}

+

+void WINAPI DbgOutString(LPCTSTR psz)

+{

+    if (m_hOutput != INVALID_HANDLE_VALUE) {

+        UINT  cb = lstrlen(psz);

+        DWORD dw;

+#ifdef UNICODE

+        CHAR szDest[2048];

+        WideCharToMultiByte(CP_ACP, 0, psz, -1, szDest, NUMELMS(szDest), 0, 0);

+        WriteFile (m_hOutput, szDest, cb, &dw, NULL);

+#else

+        WriteFile (m_hOutput, psz, cb, &dw, NULL);

+#endif

+    } else {

+        OutputDebugString (psz);

+    }

+}

+

+

+

+

+HRESULT  DbgUniqueProcessName(LPCTSTR inName, LPTSTR outName)

+{

+    HRESULT hr = S_OK;

+    const TCHAR *pIn = inName;

+    int dotPos = -1;

+

+    //scan the input and record the last '.' position

+    while (*pIn && (pIn - inName) < MAX_PATH)

+    {

+        if ( TEXT('.') == *pIn )

+            dotPos = (int)(pIn-inName);

+        ++pIn;

+    }

+

+    if (*pIn) //input should be zero-terminated within MAX_PATH

+        return E_INVALIDARG;

+

+    DWORD dwProcessId = GetCurrentProcessId();

+

+    if (dotPos < 0) 

+    {

+        //no extension in the input, appending process id to the input

+        hr = StringCchPrintf(outName, MAX_PATH, TEXT("%s_%d"), inName, dwProcessId);

+    }

+    else

+    {

+        TCHAR pathAndBasename[MAX_PATH] = {0};

+        

+        //there's an extension  - zero-terminate the path and basename first by copying

+        hr = StringCchCopyN(pathAndBasename, MAX_PATH, inName, (size_t)dotPos);

+

+        //re-combine path, basename and extension with processId appended to a basename

+        if (SUCCEEDED(hr))

+            hr = StringCchPrintf(outName, MAX_PATH, TEXT("%s_%d%s"), pathAndBasename, dwProcessId, inName + dotPos);

+    }

+

+    return hr;

+}

+

+

+/* Called by DbgInitGlobalSettings to setup alternate logging destinations

+ */

+

+void WINAPI DbgInitLogTo (

+    HKEY hKey)

+{

+    LONG  lReturn;

+    DWORD dwKeyType;

+    DWORD dwKeySize;

+    TCHAR szFile[MAX_PATH] = {0};

+    static const TCHAR cszKey[] = TEXT("LogToFile");

+

+    dwKeySize = MAX_PATH;

+    lReturn = RegQueryValueEx(

+        hKey,                       // Handle to an open key

+        cszKey,                     // Subkey name derivation

+        NULL,                       // Reserved field

+        &dwKeyType,                 // Returns the field type

+        (LPBYTE) szFile,            // Returns the field's value

+        &dwKeySize);                // Number of bytes transferred

+

+    // create an empty key if it does not already exist

+    //

+    if (lReturn != ERROR_SUCCESS || dwKeyType != REG_SZ)

+       {

+       dwKeySize = sizeof(TCHAR);

+       lReturn = RegSetValueEx(

+            hKey,                   // Handle of an open key

+            cszKey,                 // Address of subkey name

+            (DWORD) 0,              // Reserved field

+            REG_SZ,                 // Type of the key field

+            (PBYTE)szFile,          // Value for the field

+            dwKeySize);            // Size of the field buffer

+       }

+

+    // if an output-to was specified.  try to open it.

+    //

+    if (m_hOutput != INVALID_HANDLE_VALUE) {

+       EXECUTE_ASSERT(CloseHandle (m_hOutput));

+       m_hOutput = INVALID_HANDLE_VALUE;

+    }

+    if (szFile[0] != 0)

+       {

+       if (!lstrcmpi(szFile, TEXT("Console"))) {

+          m_hOutput = GetStdHandle (STD_OUTPUT_HANDLE);

+          if (m_hOutput == INVALID_HANDLE_VALUE) {

+             AllocConsole ();

+             m_hOutput = GetStdHandle (STD_OUTPUT_HANDLE);

+          }

+          SetConsoleTitle (TEXT("ActiveX Debug Output"));

+       } else if (szFile[0] &&

+                lstrcmpi(szFile, TEXT("Debug")) &&

+                lstrcmpi(szFile, TEXT("Debugger")) &&

+                lstrcmpi(szFile, TEXT("Deb")))

+          {

+            m_hOutput = CreateFile(szFile, GENERIC_WRITE,

+                                 FILE_SHARE_READ,

+                                 NULL, OPEN_ALWAYS,

+                                 FILE_ATTRIBUTE_NORMAL,

+                                 NULL);

+

+            if (INVALID_HANDLE_VALUE == m_hOutput &&

+                GetLastError() == ERROR_SHARING_VIOLATION)

+            {

+               TCHAR uniqueName[MAX_PATH] = {0};

+               if (SUCCEEDED(DbgUniqueProcessName(szFile, uniqueName)))

+               {

+                    m_hOutput = CreateFile(uniqueName, GENERIC_WRITE,

+                                         FILE_SHARE_READ,

+                                         NULL, OPEN_ALWAYS,

+                                         FILE_ATTRIBUTE_NORMAL,

+                                         NULL);

+               }

+            }

+               

+            if (INVALID_HANDLE_VALUE != m_hOutput)

+            {

+              static const TCHAR cszBar[] = TEXT("\r\n\r\n=====DbgInitialize()=====\r\n\r\n");

+              SetFilePointer (m_hOutput, 0, NULL, FILE_END);

+              DbgOutString (cszBar);

+            }

+          }

+       }

+}

+

+

+

+/* This is called by DbgInitLogLevels to read the global debug settings for

+   each logging category for this module from the registry. Normally each

+   module has it's own values set for it's different debug categories but

+   setting the global SOFTWARE\Debug\Global applies them to ALL modules */

+

+void WINAPI DbgInitGlobalSettings(bool fTakeMax)

+{

+    LONG lReturn;               // Create key return value

+    TCHAR szInfo[iDEBUGINFO];   // Constructs key names

+    HKEY hGlobalKey;            // Global override key

+

+    /* Construct the global base key name */

+    (void)StringCchPrintf(szInfo,NUMELMS(szInfo),TEXT("%s\\%s"),pBaseKey,pGlobalKey);

+

+    /* Create or open the key for this module */

+    lReturn = RegCreateKeyEx(HKEY_LOCAL_MACHINE,   // Handle of an open key

+                             szInfo,               // Address of subkey name

+                             (DWORD) 0,            // Reserved value

+                             NULL,                 // Address of class name

+                             (DWORD) 0,            // Special options flags

+                             GENERIC_READ | GENERIC_WRITE,   // Desired security access

+                             NULL,                 // Key security descriptor

+                             &hGlobalKey,          // Opened handle buffer

+                             NULL);                // What really happened

+

+    if (lReturn != ERROR_SUCCESS) {

+        lReturn = RegCreateKeyEx(HKEY_LOCAL_MACHINE,   // Handle of an open key

+                                 szInfo,               // Address of subkey name

+                                 (DWORD) 0,            // Reserved value

+                                 NULL,                 // Address of class name

+                                 (DWORD) 0,            // Special options flags

+                                 GENERIC_READ,         // Desired security access

+                                 NULL,                 // Key security descriptor

+                                 &hGlobalKey,          // Opened handle buffer

+                                 NULL);                // What really happened

+        if (lReturn != ERROR_SUCCESS) {

+            DbgLog((LOG_ERROR,1,TEXT("Could not access GLOBAL module key")));

+        }

+        return;

+    }

+

+    DbgInitKeyLevels(hGlobalKey, fTakeMax);

+    RegCloseKey(hGlobalKey);

+}

+

+

+/* This sets the debugging log levels for the different categories. We start

+   by opening (or creating if not already available) the SOFTWARE\Debug key

+   that all these settings live under. We then look at the global values

+   set under SOFTWARE\Debug\Global which apply on top of the individual

+   module settings. We then load the individual module registry settings */

+

+void WINAPI DbgInitModuleSettings(bool fTakeMax)

+{

+    LONG lReturn;               // Create key return value

+    TCHAR szInfo[iDEBUGINFO];   // Constructs key names

+    HKEY hModuleKey;            // Module key handle

+

+    /* Construct the base key name */

+    (void)StringCchPrintf(szInfo,NUMELMS(szInfo),TEXT("%s\\%s"),pBaseKey,m_ModuleName);

+

+    /* Create or open the key for this module */

+    lReturn = RegCreateKeyEx(HKEY_LOCAL_MACHINE,   // Handle of an open key

+                             szInfo,               // Address of subkey name

+                             (DWORD) 0,            // Reserved value

+                             NULL,                 // Address of class name

+                             (DWORD) 0,            // Special options flags

+                             GENERIC_READ | GENERIC_WRITE, // Desired security access

+                             NULL,                 // Key security descriptor

+                             &hModuleKey,          // Opened handle buffer

+                             NULL);                // What really happened

+

+    if (lReturn != ERROR_SUCCESS) {

+        lReturn = RegCreateKeyEx(HKEY_LOCAL_MACHINE,   // Handle of an open key

+                                 szInfo,               // Address of subkey name

+                                 (DWORD) 0,            // Reserved value

+                                 NULL,                 // Address of class name

+                                 (DWORD) 0,            // Special options flags

+                                 GENERIC_READ,         // Desired security access

+                                 NULL,                 // Key security descriptor

+                                 &hModuleKey,          // Opened handle buffer

+                                 NULL);                // What really happened

+        if (lReturn != ERROR_SUCCESS) {

+            DbgLog((LOG_ERROR,1,TEXT("Could not access module key")));

+        }

+        return;

+    }

+

+    DbgInitLogTo(hModuleKey);

+    DbgInitKeyLevels(hModuleKey, fTakeMax);

+    RegCloseKey(hModuleKey);

+}

+

+

+/* Initialise the module file name */

+

+void WINAPI DbgInitModuleName()

+{

+    TCHAR FullName[iDEBUGINFO];     // Load the full path and module name

+    LPTSTR pName;                   // Searches from the end for a backslash

+

+    GetModuleFileName(m_hInst,FullName,iDEBUGINFO);

+    pName = _tcsrchr(FullName,'\\');

+    if (pName == NULL) {

+        pName = FullName;

+    } else {

+        pName++;

+    }

+    (void)StringCchCopy(m_ModuleName,NUMELMS(m_ModuleName), pName);

+}

+

+struct MsgBoxMsg

+{

+    HWND hwnd;

+    LPCTSTR szTitle;

+    LPCTSTR szMessage;

+    DWORD dwFlags;

+    INT iResult;

+};

+

+//

+// create a thread to call MessageBox(). calling MessageBox() on

+// random threads at bad times can confuse the host (eg IE).

+//

+DWORD WINAPI MsgBoxThread(

+  __inout LPVOID lpParameter   // thread data

+  )

+{

+    MsgBoxMsg *pmsg = (MsgBoxMsg *)lpParameter;

+    pmsg->iResult = MessageBox(

+        pmsg->hwnd,

+        pmsg->szTitle,

+        pmsg->szMessage,

+        pmsg->dwFlags);

+

+    return 0;

+}

+

+INT MessageBoxOtherThread(

+    HWND hwnd,

+    LPCTSTR szTitle,

+    LPCTSTR szMessage,

+    DWORD dwFlags)

+{

+    if(g_fDbgInDllEntryPoint)

+    {

+        // can't wait on another thread because we have the loader

+        // lock held in the dll entry point.

+        // This can crash sometimes so just skip it

+        // return MessageBox(hwnd, szTitle, szMessage, dwFlags);

+        return IDCANCEL;

+    }

+    else

+    {

+        MsgBoxMsg msg = {hwnd, szTitle, szMessage, dwFlags, 0};

+        DWORD dwid;

+        HANDLE hThread = CreateThread(

+            0,                      // security

+            0,                      // stack size

+            MsgBoxThread,

+            (void *)&msg,           // arg

+            0,                      // flags

+            &dwid);

+        if(hThread)

+        {

+            WaitForSingleObject(hThread, INFINITE);

+            CloseHandle(hThread);

+            return msg.iResult;

+        }

+

+        // break into debugger on failure.

+        return IDCANCEL;

+    }

+}

+

+/* Displays a message box if the condition evaluated to FALSE */

+

+void WINAPI DbgAssert(LPCTSTR pCondition,LPCTSTR pFileName,INT iLine)

+{

+    if(g_fUseKASSERT)

+    {

+        DbgKernelAssert(pCondition, pFileName, iLine);

+    }

+    else

+    {

+

+        TCHAR szInfo[iDEBUGINFO];

+

+        (void)StringCchPrintf(szInfo, NUMELMS(szInfo),TEXT("%s \nAt line %d of %s\nContinue? (Cancel to debug)"),

+                 pCondition, iLine, pFileName);

+

+        INT MsgId = MessageBoxOtherThread(NULL,szInfo,TEXT("ASSERT Failed"),

+                                          MB_SYSTEMMODAL |

+                                          MB_ICONHAND |

+                                          MB_YESNOCANCEL |

+                                          MB_SETFOREGROUND);

+        switch (MsgId)

+        {

+          case IDNO:              /* Kill the application */

+

+              FatalAppExit(FALSE, TEXT("Application terminated"));

+              break;

+

+          case IDCANCEL:          /* Break into the debugger */

+

+              DebugBreak();

+              break;

+

+          case IDYES:             /* Ignore assertion continue execution */

+              break;

+        }

+    }

+}

+

+/* Displays a message box at a break point */

+

+void WINAPI DbgBreakPoint(LPCTSTR pCondition,LPCTSTR pFileName,INT iLine)

+{

+    if(g_fUseKASSERT)

+    {

+        DbgKernelAssert(pCondition, pFileName, iLine);

+    }

+    else

+    {

+        TCHAR szInfo[iDEBUGINFO];

+

+        (void)StringCchPrintf(szInfo, NUMELMS(szInfo),TEXT("%s \nAt line %d of %s\nContinue? (Cancel to debug)"),

+                 pCondition, iLine, pFileName);

+

+        INT MsgId = MessageBoxOtherThread(NULL,szInfo,TEXT("Hard coded break point"),

+                                          MB_SYSTEMMODAL |

+                                          MB_ICONHAND |

+                                          MB_YESNOCANCEL |

+                                          MB_SETFOREGROUND);

+        switch (MsgId)

+        {

+          case IDNO:              /* Kill the application */

+

+              FatalAppExit(FALSE, TEXT("Application terminated"));

+              break;

+

+          case IDCANCEL:          /* Break into the debugger */

+

+              DebugBreak();

+              break;

+

+          case IDYES:             /* Ignore break point continue execution */

+              break;

+        }

+    }

+}

+

+void WINAPI DbgBreakPoint(LPCTSTR pFileName,INT iLine,__format_string LPCTSTR szFormatString,...)

+{

+    // A debug break point message can have at most 2000 characters if

+    // ANSI or UNICODE characters are being used.  A debug break point message

+    // can have between 1000 and 2000 double byte characters in it.  If a

+    // particular message needs more characters, then the value of this constant

+    // should be increased.

+    const DWORD MAX_BREAK_POINT_MESSAGE_SIZE = 2000;

+

+    TCHAR szBreakPointMessage[MAX_BREAK_POINT_MESSAGE_SIZE];

+

+    va_list va;

+    va_start( va, szFormatString );

+

+    HRESULT hr = StringCchVPrintf( szBreakPointMessage, NUMELMS(szBreakPointMessage), szFormatString, va );

+

+    va_end(va);

+

+    if( FAILED(hr) ) {

+        DbgBreak( "ERROR in DbgBreakPoint().  The variable length debug message could not be displayed because StringCchVPrintf() failed." );

+        return;

+    }

+

+    ::DbgBreakPoint( szBreakPointMessage, pFileName, iLine );

+}

+

+

+/* When we initialised the library we stored in the m_Levels array the current

+   debug output level for this module for each of the five categories. When

+   some debug logging is sent to us it can be sent with a combination of the

+   categories (if it is applicable to many for example) in which case we map

+   the type's categories into their current debug levels and see if any of

+   them can be accepted. The function looks at each bit position in turn from

+   the input type field and then compares it's debug level with the modules.

+

+   A level of 0 means that output is always sent to the debugger.  This is

+   due to producing output if the input level is <= m_Levels.

+*/

+

+

+BOOL WINAPI DbgCheckModuleLevel(DWORD Type,DWORD Level)

+{

+    if(g_fAutoRefreshLevels)

+    {

+        // re-read the registry every second. We cannot use RegNotify() to

+        // notice registry changes because it's not available on win9x.

+        static DWORD g_dwLastRefresh = 0;

+        DWORD dwTime = timeGetTime();

+        if(dwTime - g_dwLastRefresh > 1000) {

+            g_dwLastRefresh = dwTime;

+

+            // there's a race condition: multiple threads could update the

+            // values. plus read and write not synchronized. no harm

+            // though.

+            DbgInitModuleSettings(false);

+        }

+    }

+

+

+    DWORD Mask = 0x01;

+

+    // If no valid bits are set return FALSE

+    if ((Type & ((1<<iMAXLEVELS)-1))) {

+

+	// speed up unconditional output.

+	if (0==Level)

+	    return(TRUE);

+	

+        for (LONG lKeyPos = 0;lKeyPos < iMAXLEVELS;lKeyPos++) {

+            if (Type & Mask) {

+                if (Level <= (m_Levels[lKeyPos] & ~LOG_FORCIBLY_SET)) {

+                    return TRUE;

+                }

+            }

+            Mask <<= 1;

+        }

+    }

+    return FALSE;

+}

+

+

+/* Set debug levels to a given value */

+

+void WINAPI DbgSetModuleLevel(DWORD Type, DWORD Level)

+{

+    DWORD Mask = 0x01;

+

+    for (LONG lKeyPos = 0;lKeyPos < iMAXLEVELS;lKeyPos++) {

+        if (Type & Mask) {

+            m_Levels[lKeyPos] = Level | LOG_FORCIBLY_SET;

+        }

+        Mask <<= 1;

+    }

+}

+

+/* whether to check registry values periodically. this isn't turned

+   automatically because of the potential performance hit. */

+void WINAPI DbgSetAutoRefreshLevels(bool fAuto)

+{

+    g_fAutoRefreshLevels = fAuto;

+}

+

+#ifdef UNICODE

+//

+// warning -- this function is implemented twice for ansi applications

+// linking to the unicode library

+//

+void WINAPI DbgLogInfo(DWORD Type,DWORD Level,__format_string LPCSTR pFormat,...)

+{

+    /* Check the current level for this type combination */

+

+    BOOL bAccept = DbgCheckModuleLevel(Type,Level);

+    if (bAccept == FALSE) {

+        return;

+    }

+

+    TCHAR szInfo[2000];

+

+    /* Format the variable length parameter list */

+

+    va_list va;

+    va_start(va, pFormat);

+

+    (void)StringCchPrintf(szInfo, NUMELMS(szInfo),

+             TEXT("%s(tid %x) %8d : "),

+             m_ModuleName,

+             GetCurrentThreadId(), timeGetTime() - dwTimeOffset);

+

+    CHAR szInfoA[2000];

+    WideCharToMultiByte(CP_ACP, 0, szInfo, -1, szInfoA, NUMELMS(szInfoA), 0, 0);

+

+    (void)StringCchVPrintfA(szInfoA + lstrlenA(szInfoA), NUMELMS(szInfoA) - lstrlenA(szInfoA), pFormat, va);

+    (void)StringCchCatA(szInfoA, NUMELMS(szInfoA), "\r\n");

+

+    WCHAR wszOutString[2000];

+    MultiByteToWideChar(CP_ACP, 0, szInfoA, -1, wszOutString, NUMELMS(wszOutString));

+    DbgOutString(wszOutString);

+

+    va_end(va);

+}

+

+void WINAPI DbgAssert(LPCSTR pCondition,LPCSTR pFileName,INT iLine)

+{

+    if(g_fUseKASSERT)

+    {

+        DbgKernelAssert(pCondition, pFileName, iLine);

+    }

+    else

+    {

+

+        TCHAR szInfo[iDEBUGINFO];

+

+        (void)StringCchPrintf(szInfo, NUMELMS(szInfo), TEXT("%hs \nAt line %d of %hs\nContinue? (Cancel to debug)"),

+                 pCondition, iLine, pFileName);

+

+        INT MsgId = MessageBoxOtherThread(NULL,szInfo,TEXT("ASSERT Failed"),

+                                          MB_SYSTEMMODAL |

+                                          MB_ICONHAND |

+                                          MB_YESNOCANCEL |

+                                          MB_SETFOREGROUND);

+        switch (MsgId)

+        {

+          case IDNO:              /* Kill the application */

+

+              FatalAppExit(FALSE, TEXT("Application terminated"));

+              break;

+

+          case IDCANCEL:          /* Break into the debugger */

+

+              DebugBreak();

+              break;

+

+          case IDYES:             /* Ignore assertion continue execution */

+              break;

+        }

+    }

+}

+

+/* Displays a message box at a break point */

+

+void WINAPI DbgBreakPoint(LPCSTR pCondition,LPCSTR pFileName,INT iLine)

+{

+    if(g_fUseKASSERT)

+    {

+        DbgKernelAssert(pCondition, pFileName, iLine);

+    }

+    else

+    {

+        TCHAR szInfo[iDEBUGINFO];

+

+        (void)StringCchPrintf(szInfo, NUMELMS(szInfo),TEXT("%hs \nAt line %d of %hs\nContinue? (Cancel to debug)"),

+                 pCondition, iLine, pFileName);

+

+        INT MsgId = MessageBoxOtherThread(NULL,szInfo,TEXT("Hard coded break point"),

+                                          MB_SYSTEMMODAL |

+                                          MB_ICONHAND |

+                                          MB_YESNOCANCEL |

+                                          MB_SETFOREGROUND);

+        switch (MsgId)

+        {

+          case IDNO:              /* Kill the application */

+

+              FatalAppExit(FALSE, TEXT("Application terminated"));

+              break;

+

+          case IDCANCEL:          /* Break into the debugger */

+

+              DebugBreak();

+              break;

+

+          case IDYES:             /* Ignore break point continue execution */

+              break;

+        }

+    }

+}

+

+void WINAPI DbgKernelAssert(LPCSTR pCondition,LPCSTR pFileName,INT iLine)

+{

+    DbgLog((LOG_ERROR,0,TEXT("Assertion FAILED (%hs) at line %d in file %hs"),

+           pCondition, iLine, pFileName));

+    DebugBreak();

+}

+

+#endif

+

+/* Print a formatted string to the debugger prefixed with this module's name

+   Because the COMBASE classes are linked statically every module loaded will

+   have their own copy of this code. It therefore helps if the module name is

+   included on the output so that the offending code can be easily found */

+

+//

+// warning -- this function is implemented twice for ansi applications

+// linking to the unicode library

+//

+void WINAPI DbgLogInfo(DWORD Type,DWORD Level,LPCTSTR pFormat,...)

+{

+

+    /* Check the current level for this type combination */

+

+    BOOL bAccept = DbgCheckModuleLevel(Type,Level);

+    if (bAccept == FALSE) {

+        return;

+    }

+

+    TCHAR szInfo[2000];

+

+    /* Format the variable length parameter list */

+

+    va_list va;

+    va_start(va, pFormat);

+

+    (void)StringCchPrintf(szInfo, NUMELMS(szInfo),

+             TEXT("%s(tid %x) %8d : "),

+             m_ModuleName,

+             GetCurrentThreadId(), timeGetTime() - dwTimeOffset);

+

+    (void)StringCchVPrintf(szInfo + lstrlen(szInfo), NUMELMS(szInfo) - lstrlen(szInfo), pFormat, va);

+    (void)StringCchCat(szInfo, NUMELMS(szInfo), TEXT("\r\n"));

+    DbgOutString(szInfo);

+

+    va_end(va);

+}

+

+

+/* If we are executing as a pure kernel filter we cannot display message

+   boxes to the user, this provides an alternative which puts the error

+   condition on the debugger output with a suitable eye catching message */

+

+void WINAPI DbgKernelAssert(LPCTSTR pCondition,LPCTSTR pFileName,INT iLine)

+{

+    DbgLog((LOG_ERROR,0,TEXT("Assertion FAILED (%s) at line %d in file %s"),

+           pCondition, iLine, pFileName));

+    DebugBreak();

+}

+

+

+

+/* Each time we create an object derived from CBaseObject the constructor will

+   call us to register the creation of the new object. We are passed a string

+   description which we store away. We return a cookie that the constructor

+   uses to identify the object when it is destroyed later on. We update the

+   total number of active objects in the DLL mainly for debugging purposes */

+

+DWORD WINAPI DbgRegisterObjectCreation(LPCSTR szObjectName,

+                                       LPCWSTR wszObjectName)

+{

+    /* If this fires you have a mixed DEBUG/RETAIL build */

+

+    ASSERT(!!szObjectName ^ !!wszObjectName);

+

+    /* Create a place holder for this object description */

+

+    ObjectDesc *pObject = new ObjectDesc;

+    ASSERT(pObject);

+

+    /* It is valid to pass a NULL object name */

+    if (pObject == NULL) {

+        return FALSE;

+    }

+

+    /* Check we have been initialised - we may not be initialised when we are

+       being pulled in from an executable which has globally defined objects

+       as they are created by the C++ run time before WinMain is called */

+

+    if (m_bInit == FALSE) {

+        DbgInitialise(GetModuleHandle(NULL));

+    }

+

+    /* Grab the list critical section */

+    EnterCriticalSection(&m_CSDebug);

+

+    /* If no name then default to UNKNOWN */

+    if (!szObjectName && !wszObjectName) {

+        szObjectName = pUnknownName;

+    }

+

+    /* Put the new description at the head of the list */

+

+    pObject->m_szName = szObjectName;

+    pObject->m_wszName = wszObjectName;

+    pObject->m_dwCookie = ++m_dwNextCookie;

+    pObject->m_pNext = pListHead;

+

+    pListHead = pObject;

+    m_dwObjectCount++;

+

+    DWORD ObjectCookie = pObject->m_dwCookie;

+    ASSERT(ObjectCookie);

+

+    if(wszObjectName) {

+        DbgLog((LOG_MEMORY,2,TEXT("Object created   %d (%ls) %d Active"),

+                pObject->m_dwCookie, wszObjectName, m_dwObjectCount));

+    } else {

+        DbgLog((LOG_MEMORY,2,TEXT("Object created   %d (%hs) %d Active"),

+                pObject->m_dwCookie, szObjectName, m_dwObjectCount));

+    }

+

+    LeaveCriticalSection(&m_CSDebug);

+    return ObjectCookie;

+}

+

+

+/* This is called by the CBaseObject destructor when an object is about to be

+   destroyed, we are passed the cookie we returned during construction that

+   identifies this object. We scan the object list for a matching cookie and

+   remove the object if successful. We also update the active object count */

+

+BOOL WINAPI DbgRegisterObjectDestruction(DWORD dwCookie)

+{

+    /* Grab the list critical section */

+    EnterCriticalSection(&m_CSDebug);

+

+    ObjectDesc *pObject = pListHead;

+    ObjectDesc *pPrevious = NULL;

+

+    /* Scan the object list looking for a cookie match */

+

+    while (pObject) {

+        if (pObject->m_dwCookie == dwCookie) {

+            break;

+        }

+        pPrevious = pObject;

+        pObject = pObject->m_pNext;

+    }

+

+    if (pObject == NULL) {

+        DbgBreak("Apparently destroying a bogus object");

+        LeaveCriticalSection(&m_CSDebug);

+        return FALSE;

+    }

+

+    /* Is the object at the head of the list */

+

+    if (pPrevious == NULL) {

+        pListHead = pObject->m_pNext;

+    } else {

+        pPrevious->m_pNext = pObject->m_pNext;

+    }

+

+    /* Delete the object and update the housekeeping information */

+

+    m_dwObjectCount--;

+

+    if(pObject->m_wszName) {

+        DbgLog((LOG_MEMORY,2,TEXT("Object destroyed %d (%ls) %d Active"),

+                pObject->m_dwCookie, pObject->m_wszName, m_dwObjectCount));

+    } else {

+        DbgLog((LOG_MEMORY,2,TEXT("Object destroyed %d (%hs) %d Active"),

+                pObject->m_dwCookie, pObject->m_szName, m_dwObjectCount));

+    }

+

+    delete pObject;

+    LeaveCriticalSection(&m_CSDebug);

+    return TRUE;

+}

+

+

+/* This runs through the active object list displaying their details */

+

+void WINAPI DbgDumpObjectRegister()

+{

+    TCHAR szInfo[iDEBUGINFO];

+

+    /* Grab the list critical section */

+

+    EnterCriticalSection(&m_CSDebug);

+    ObjectDesc *pObject = pListHead;

+

+    /* Scan the object list displaying the name and cookie */

+

+    DbgLog((LOG_MEMORY,2,TEXT("")));

+    DbgLog((LOG_MEMORY,2,TEXT("   ID             Object Description")));

+    DbgLog((LOG_MEMORY,2,TEXT("")));

+

+    while (pObject) {

+        if(pObject->m_wszName) {

+            (void)StringCchPrintf(szInfo,NUMELMS(szInfo),TEXT("%5d (%p) %30ls"),pObject->m_dwCookie, &pObject, pObject->m_wszName);

+        } else {

+            (void)StringCchPrintf(szInfo,NUMELMS(szInfo),TEXT("%5d (%p) %30hs"),pObject->m_dwCookie, &pObject, pObject->m_szName);

+        }

+        DbgLog((LOG_MEMORY,2,szInfo));

+        pObject = pObject->m_pNext;

+    }

+

+    (void)StringCchPrintf(szInfo,NUMELMS(szInfo),TEXT("Total object count %5d"),m_dwObjectCount);

+    DbgLog((LOG_MEMORY,2,TEXT("")));

+    DbgLog((LOG_MEMORY,1,szInfo));

+    LeaveCriticalSection(&m_CSDebug);

+}

+

+/*  Debug infinite wait stuff */

+DWORD WINAPI DbgWaitForSingleObject(HANDLE h)

+{

+    DWORD dwWaitResult;

+    do {

+        dwWaitResult = WaitForSingleObject(h, dwWaitTimeout);

+        ASSERT(dwWaitResult == WAIT_OBJECT_0);

+    } while (dwWaitResult == WAIT_TIMEOUT);

+    return dwWaitResult;

+}

+DWORD WINAPI DbgWaitForMultipleObjects(DWORD nCount,

+                                __in_ecount(nCount) CONST HANDLE *lpHandles,

+                                BOOL bWaitAll)

+{

+    DWORD dwWaitResult;

+    do {

+        dwWaitResult = WaitForMultipleObjects(nCount,

+                                              lpHandles,

+                                              bWaitAll,

+                                              dwWaitTimeout);

+        ASSERT((DWORD)(dwWaitResult - WAIT_OBJECT_0) < MAXIMUM_WAIT_OBJECTS);

+    } while (dwWaitResult == WAIT_TIMEOUT);

+    return dwWaitResult;

+}

+

+void WINAPI DbgSetWaitTimeout(DWORD dwTimeout)

+{

+    dwWaitTimeout = dwTimeout;

+}

+

+#endif /* DEBUG */

+

+#ifdef _OBJBASE_H_

+

+    /*  Stuff for printing out our GUID names */

+

+    GUID_STRING_ENTRY g_GuidNames[] = {

+    #define OUR_GUID_ENTRY(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \

+    { #name, { l, w1, w2, { b1, b2,  b3,  b4,  b5,  b6,  b7,  b8 } } },

+        #include <uuids.h>

+    };

+

+    CGuidNameList GuidNames;

+    int g_cGuidNames = sizeof(g_GuidNames) / sizeof(g_GuidNames[0]);

+

+    char *CGuidNameList::operator [] (const GUID &guid)

+    {

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

+            if (g_GuidNames[i].guid == guid) {

+                return g_GuidNames[i].szName;

+            }

+        }

+        if (guid == GUID_NULL) {

+            return "GUID_NULL";

+        }

+

+	// !!! add something to print FOURCC guids?

+	

+	// shouldn't this print the hex CLSID?

+        return "Unknown GUID Name";

+    }

+

+#endif /* _OBJBASE_H_ */

+

+/*  CDisp class - display our data types */

+

+// clashes with REFERENCE_TIME

+CDisp::CDisp(LONGLONG ll, int Format)

+{

+    // note: this could be combined with CDisp(LONGLONG) by

+    // introducing a default format of CDISP_REFTIME

+    LARGE_INTEGER li;

+    li.QuadPart = ll;

+    switch (Format) {

+	case CDISP_DEC:

+	{

+	    TCHAR  temp[20];

+	    int pos=20;

+	    temp[--pos] = 0;

+	    int digit;

+	    // always output at least one digit

+	    do {

+		// Get the rightmost digit - we only need the low word

+	        digit = li.LowPart % 10;

+		li.QuadPart /= 10;

+		temp[--pos] = (TCHAR) digit+L'0';

+	    } while (li.QuadPart);

+	    (void)StringCchCopy(m_String, NUMELMS(m_String), temp+pos);

+	    break;

+	}

+	case CDISP_HEX:

+	default:

+	    (void)StringCchPrintf(m_String, NUMELMS(m_String), TEXT("0x%X%8.8X"), li.HighPart, li.LowPart);

+    }

+};

+

+CDisp::CDisp(REFCLSID clsid)

+{

+#ifdef UNICODE 

+    (void)StringFromGUID2(clsid, m_String, NUMELMS(m_String));

+#else

+    WCHAR wszTemp[50];

+    (void)StringFromGUID2(clsid, wszTemp, NUMELMS(wszTemp));

+    (void)StringCchPrintf(m_String, NUMELMS(m_String), TEXT("%S"), wszTemp);

+#endif

+};

+

+#ifdef __STREAMS__

+/*  Display stuff */

+CDisp::CDisp(CRefTime llTime)

+{

+    LONGLONG llDiv;

+    if (llTime < 0) {

+        llTime = -llTime;

+        (void)StringCchCopy(m_String, NUMELMS(m_String), TEXT("-"));

+    }

+    llDiv = (LONGLONG)24 * 3600 * 10000000;

+    if (llTime >= llDiv) {

+        (void)StringCchPrintf(m_String + lstrlen(m_String), NUMELMS(m_String) - lstrlen(m_String), TEXT("%d days "), (LONG)(llTime / llDiv));

+        llTime = llTime % llDiv;

+    }

+    llDiv = (LONGLONG)3600 * 10000000;

+    if (llTime >= llDiv) {

+        (void)StringCchPrintf(m_String + lstrlen(m_String), NUMELMS(m_String) - lstrlen(m_String), TEXT("%d hrs "), (LONG)(llTime / llDiv));

+        llTime = llTime % llDiv;

+    }

+    llDiv = (LONGLONG)60 * 10000000;

+    if (llTime >= llDiv) {

+        (void)StringCchPrintf(m_String + lstrlen(m_String), NUMELMS(m_String) - lstrlen(m_String), TEXT("%d mins "), (LONG)(llTime / llDiv));

+        llTime = llTime % llDiv;

+    }

+    (void)StringCchPrintf(m_String + lstrlen(m_String), NUMELMS(m_String) - lstrlen(m_String), TEXT("%d.%3.3d sec"),

+             (LONG)llTime / 10000000,

+             (LONG)((llTime % 10000000) / 10000));

+};

+

+#endif // __STREAMS__

+

+

+/*  Display pin */

+CDisp::CDisp(IPin *pPin)

+{

+    PIN_INFO pi;

+    TCHAR str[MAX_PIN_NAME];

+    CLSID clsid;

+

+    if (pPin) {

+       pPin->QueryPinInfo(&pi);

+       pi.pFilter->GetClassID(&clsid);

+       QueryPinInfoReleaseFilter(pi);

+      #ifndef UNICODE

+       WideCharToMultiByte(GetACP(), 0, pi.achName, lstrlenW(pi.achName) + 1,

+                           str, MAX_PIN_NAME, NULL, NULL);

+      #else

+       (void)StringCchCopy(str, NUMELMS(str), pi.achName);

+      #endif

+    } else {

+       (void)StringCchCopy(str, NUMELMS(str), TEXT("NULL IPin"));

+    }

+

+    m_pString = (PTCHAR) new TCHAR[lstrlen(str)+64];

+    if (!m_pString) {

+	return;

+    }

+

+    (void)StringCchPrintf(m_pString, lstrlen(str) + 64, TEXT("%hs(%s)"), GuidNames[clsid], str);

+}

+

+/*  Display filter or pin */

+CDisp::CDisp(IUnknown *pUnk)

+{

+    IBaseFilter *pf;

+    HRESULT hr = pUnk->QueryInterface(IID_IBaseFilter, (void **)&pf);

+    if(SUCCEEDED(hr))

+    {

+        FILTER_INFO fi;

+        hr = pf->QueryFilterInfo(&fi);

+        if(SUCCEEDED(hr))

+        {

+            QueryFilterInfoReleaseGraph(fi);

+

+            size_t len = lstrlenW(fi.achName)  + 1;

+

+            m_pString = new TCHAR[len];

+            if(m_pString)

+            {

+#ifdef UNICODE

+                (void)StringCchCopy(m_pString, len, fi.achName);

+#else

+                (void)StringCchPrintf(m_pString, len, "%S", fi.achName);

+#endif

+            }

+        }

+

+        pf->Release();

+

+        return;

+    }

+

+    IPin *pp;

+    hr = pUnk->QueryInterface(IID_IPin, (void **)&pp);

+    if(SUCCEEDED(hr))

+    {

+        CDisp::CDisp(pp);

+        pp->Release();

+        return;

+    }

+}

+

+

+CDisp::~CDisp()

+{

+}

+

+CDispBasic::~CDispBasic()

+{

+    if (m_pString != m_String) {

+	delete [] m_pString;

+    }

+}

+

+CDisp::CDisp(double d)

+{

+    (void)StringCchPrintf(m_String, NUMELMS(m_String), TEXT("%d.%03d"), (int) d, (int) ((d - (int) d) * 1000));

+}

+

+

+/* If built for debug this will display the media type details. We convert the

+   major and subtypes into strings and also ask the base classes for a string

+   description of the subtype, so MEDIASUBTYPE_RGB565 becomes RGB 565 16 bit

+   We also display the fields in the BITMAPINFOHEADER structure, this should

+   succeed as we do not accept input types unless the format is big enough */

+

+#ifdef DEBUG

+void WINAPI DisplayType(LPCTSTR label, const AM_MEDIA_TYPE *pmtIn)

+{

+

+    /* Dump the GUID types and a short description */

+

+    DbgLog((LOG_TRACE,5,TEXT("")));

+    DbgLog((LOG_TRACE,2,TEXT("%s  M type %hs  S type %hs"), label,

+	    GuidNames[pmtIn->majortype],

+	    GuidNames[pmtIn->subtype]));

+    DbgLog((LOG_TRACE,5,TEXT("Subtype description %s"),GetSubtypeName(&pmtIn->subtype)));

+

+    /* Dump the generic media types */

+

+    if (pmtIn->bTemporalCompression) {

+        DbgLog((LOG_TRACE,5,TEXT("Temporally compressed")));

+    } else {

+        DbgLog((LOG_TRACE,5,TEXT("Not temporally compressed")));

+    }

+

+    if (pmtIn->bFixedSizeSamples) {

+        DbgLog((LOG_TRACE,5,TEXT("Sample size %d"),pmtIn->lSampleSize));

+    } else {

+        DbgLog((LOG_TRACE,5,TEXT("Variable size samples")));

+    }

+

+    if (pmtIn->formattype == FORMAT_VideoInfo) {

+

+        VIDEOINFOHEADER *pVideoInfo = (VIDEOINFOHEADER *)pmtIn->pbFormat;

+

+        DisplayRECT(TEXT("Source rectangle"),pVideoInfo->rcSource);

+        DisplayRECT(TEXT("Target rectangle"),pVideoInfo->rcTarget);

+        DisplayBITMAPINFO(HEADER(pmtIn->pbFormat));

+

+    } if (pmtIn->formattype == FORMAT_VideoInfo2) {

+

+        VIDEOINFOHEADER2 *pVideoInfo2 = (VIDEOINFOHEADER2 *)pmtIn->pbFormat;

+

+        DisplayRECT(TEXT("Source rectangle"),pVideoInfo2->rcSource);

+        DisplayRECT(TEXT("Target rectangle"),pVideoInfo2->rcTarget);

+        DbgLog((LOG_TRACE, 5, TEXT("Aspect Ratio: %d:%d"),

+            pVideoInfo2->dwPictAspectRatioX,

+            pVideoInfo2->dwPictAspectRatioY));

+        DisplayBITMAPINFO(&pVideoInfo2->bmiHeader);

+

+    } else if (pmtIn->majortype == MEDIATYPE_Audio) {

+        DbgLog((LOG_TRACE,2,TEXT("     Format type %hs"),

+            GuidNames[pmtIn->formattype]));

+        DbgLog((LOG_TRACE,2,TEXT("     Subtype %hs"),

+            GuidNames[pmtIn->subtype]));

+

+        if ((pmtIn->subtype != MEDIASUBTYPE_MPEG1Packet)

+          && (pmtIn->cbFormat >= sizeof(PCMWAVEFORMAT)))

+        {

+            /* Dump the contents of the WAVEFORMATEX type-specific format structure */

+

+            WAVEFORMATEX *pwfx = (WAVEFORMATEX *) pmtIn->pbFormat;

+            DbgLog((LOG_TRACE,2,TEXT("wFormatTag %u"), pwfx->wFormatTag));

+            DbgLog((LOG_TRACE,2,TEXT("nChannels %u"), pwfx->nChannels));

+            DbgLog((LOG_TRACE,2,TEXT("nSamplesPerSec %lu"), pwfx->nSamplesPerSec));

+            DbgLog((LOG_TRACE,2,TEXT("nAvgBytesPerSec %lu"), pwfx->nAvgBytesPerSec));

+            DbgLog((LOG_TRACE,2,TEXT("nBlockAlign %u"), pwfx->nBlockAlign));

+            DbgLog((LOG_TRACE,2,TEXT("wBitsPerSample %u"), pwfx->wBitsPerSample));

+

+            /* PCM uses a WAVEFORMAT and does not have the extra size field */

+

+            if (pmtIn->cbFormat >= sizeof(WAVEFORMATEX)) {

+                DbgLog((LOG_TRACE,2,TEXT("cbSize %u"), pwfx->cbSize));

+            }

+        } else {

+        }

+

+    } else {

+        DbgLog((LOG_TRACE,2,TEXT("     Format type %hs"),

+            GuidNames[pmtIn->formattype]));

+    }

+}

+

+

+void DisplayBITMAPINFO(const BITMAPINFOHEADER* pbmi)

+{

+    DbgLog((LOG_TRACE,5,TEXT("Size of BITMAPINFO structure %d"),pbmi->biSize));

+    if (pbmi->biCompression < 256) {

+        DbgLog((LOG_TRACE,2,TEXT("%dx%dx%d bit  (%d)"),

+                pbmi->biWidth, pbmi->biHeight,

+                pbmi->biBitCount, pbmi->biCompression));

+    } else {

+        DbgLog((LOG_TRACE,2,TEXT("%dx%dx%d bit '%4.4hs'"),

+                pbmi->biWidth, pbmi->biHeight,

+                pbmi->biBitCount, &pbmi->biCompression));

+    }

+

+    DbgLog((LOG_TRACE,2,TEXT("Image size %d"),pbmi->biSizeImage));

+    DbgLog((LOG_TRACE,5,TEXT("Planes %d"),pbmi->biPlanes));

+    DbgLog((LOG_TRACE,5,TEXT("X Pels per metre %d"),pbmi->biXPelsPerMeter));

+    DbgLog((LOG_TRACE,5,TEXT("Y Pels per metre %d"),pbmi->biYPelsPerMeter));

+    DbgLog((LOG_TRACE,5,TEXT("Colours used %d"),pbmi->biClrUsed));

+}

+

+

+void DisplayRECT(LPCTSTR szLabel, const RECT& rc)

+{

+    DbgLog((LOG_TRACE,5,TEXT("%s (Left %d Top %d Right %d Bottom %d)"),

+            szLabel,

+            rc.left,

+            rc.top,

+            rc.right,

+            rc.bottom));

+}

+

+

+void WINAPI DumpGraph(IFilterGraph *pGraph, DWORD dwLevel)

+{

+    if( !pGraph )

+    {

+        return;

+    }

+

+    IEnumFilters *pFilters;

+

+    DbgLog((LOG_TRACE,dwLevel,TEXT("DumpGraph [%x]"), pGraph));

+

+    if (FAILED(pGraph->EnumFilters(&pFilters))) {

+	DbgLog((LOG_TRACE,dwLevel,TEXT("EnumFilters failed!")));

+    }

+

+    IBaseFilter *pFilter;

+    ULONG	n;

+    while (pFilters->Next(1, &pFilter, &n) == S_OK) {

+	FILTER_INFO	info;

+

+	if (FAILED(pFilter->QueryFilterInfo(&info))) {

+	    DbgLog((LOG_TRACE,dwLevel,TEXT("    Filter [%p]  -- failed QueryFilterInfo"), pFilter));

+	} else {

+	    QueryFilterInfoReleaseGraph(info);

+

+	    // !!! should QueryVendorInfo here!

+	

+	    DbgLog((LOG_TRACE,dwLevel,TEXT("    Filter [%p]  '%ls'"), pFilter, info.achName));

+

+	    IEnumPins *pins;

+

+	    if (FAILED(pFilter->EnumPins(&pins))) {

+		DbgLog((LOG_TRACE,dwLevel,TEXT("EnumPins failed!")));

+	    } else {

+

+		IPin *pPin;

+		while (pins->Next(1, &pPin, &n) == S_OK) {

+		    PIN_INFO	pinInfo;

+

+		    if (FAILED(pPin->QueryPinInfo(&pinInfo))) {

+			DbgLog((LOG_TRACE,dwLevel,TEXT("          Pin [%x]  -- failed QueryPinInfo"), pPin));

+		    } else {

+			QueryPinInfoReleaseFilter(pinInfo);

+

+			IPin *pPinConnected = NULL;

+

+			HRESULT hr = pPin->ConnectedTo(&pPinConnected);

+

+			if (pPinConnected) {

+			    DbgLog((LOG_TRACE,dwLevel,TEXT("          Pin [%p]  '%ls' [%sput]")

+							   TEXT("  Connected to pin [%p]"),

+				    pPin, pinInfo.achName,

+				    pinInfo.dir == PINDIR_INPUT ? TEXT("In") : TEXT("Out"),

+				    pPinConnected));

+

+			    pPinConnected->Release();

+

+			    // perhaps we should really dump the type both ways as a sanity

+			    // check?

+			    if (pinInfo.dir == PINDIR_OUTPUT) {

+				AM_MEDIA_TYPE mt;

+

+				hr = pPin->ConnectionMediaType(&mt);

+

+				if (SUCCEEDED(hr)) {

+				    DisplayType(TEXT("Connection type"), &mt);

+

+				    FreeMediaType(mt);

+				}

+			    }

+			} else {

+			    DbgLog((LOG_TRACE,dwLevel,

+				    TEXT("          Pin [%x]  '%ls' [%sput]"),

+				    pPin, pinInfo.achName,

+				    pinInfo.dir == PINDIR_INPUT ? TEXT("In") : TEXT("Out")));

+

+			}

+		    }

+

+		    pPin->Release();

+

+		}

+

+		pins->Release();

+	    }

+

+	}

+	

+	pFilter->Release();

+    }

+

+    pFilters->Release();

+

+}

+

+#endif

+

+#endif /* PJMEDIA_VIDEO_DEV_HAS_DSHOW */

diff --git a/jni/pjproject-android/third_party/BaseClasses/wxdebug.h b/jni/pjproject-android/third_party/BaseClasses/wxdebug.h
new file mode 100644
index 0000000..d4c69db
--- /dev/null
+++ b/jni/pjproject-android/third_party/BaseClasses/wxdebug.h
@@ -0,0 +1,359 @@
+//------------------------------------------------------------------------------

+// File: WXDebug.h

+//

+// Desc: DirectShow base classes - provides debugging facilities.

+//

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

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

+

+

+#ifndef __WXDEBUG__

+#define __WXDEBUG__

+

+// This library provides fairly straight forward debugging functionality, this

+// is split into two main sections. The first is assertion handling, there are

+// three types of assertions provided here. The most commonly used one is the

+// ASSERT(condition) macro which will pop up a message box including the file

+// and line number if the condition evaluates to FALSE. Then there is the

+// EXECUTE_ASSERT macro which is the same as ASSERT except the condition will

+// still be executed in NON debug builds. The final type of assertion is the

+// KASSERT macro which is more suitable for pure (perhaps kernel) filters as

+// the condition is printed onto the debugger rather than in a message box.

+//

+// The other part of the debug module facilties is general purpose logging.

+// This is accessed by calling DbgLog(). The function takes a type and level

+// field which define the type of informational string you are presenting and

+// it's relative importance. The type field can be a combination (one or more)

+// of LOG_TIMING, LOG_TRACE, LOG_MEMORY, LOG_LOCKING and LOG_ERROR. The level

+// is a DWORD value where zero defines highest important. Use of zero as the

+// debug logging level is to be encouraged ONLY for major errors or events as

+// they will ALWAYS be displayed on the debugger. Other debug output has it's

+// level matched against the current debug output level stored in the registry

+// for this module and if less than the current setting it will be displayed.

+//

+// Each module or executable has it's own debug output level for each of the

+// five types. These are read in when the DbgInitialise function is called

+// for DLLs linking to STRMBASE.LIB this is done automatically when the DLL

+// is loaded, executables must call it explicitely with the module instance

+// handle given to them through the WINMAIN entry point. An executable must

+// also call DbgTerminate when they have finished to clean up the resources

+// the debug library uses, once again this is done automatically for DLLs

+

+// These are the five different categories of logging information

+

+enum {  LOG_TIMING = 0x01,    // Timing and performance measurements

+        LOG_TRACE = 0x02,     // General step point call tracing

+        LOG_MEMORY =  0x04,   // Memory and object allocation/destruction

+        LOG_LOCKING = 0x08,   // Locking/unlocking of critical sections

+        LOG_ERROR = 0x10,     // Debug error notification

+        LOG_CUSTOM1 = 0x20,

+        LOG_CUSTOM2 = 0x40,

+        LOG_CUSTOM3 = 0x80,

+        LOG_CUSTOM4 = 0x100,

+        LOG_CUSTOM5 = 0x200,

+};

+

+#define LOG_FORCIBLY_SET 0x80000000

+

+enum {  CDISP_HEX = 0x01,

+        CDISP_DEC = 0x02};

+

+// For each object created derived from CBaseObject (in debug builds) we

+// create a descriptor that holds it's name (statically allocated memory)

+// and a cookie we assign it. We keep a list of all the active objects

+// we have registered so that we can dump a list of remaining objects

+

+typedef struct tag_ObjectDesc {

+    LPCSTR m_szName;

+    LPCWSTR m_wszName;

+    DWORD m_dwCookie;

+    tag_ObjectDesc *m_pNext;

+} ObjectDesc;

+

+#define DLLIMPORT __declspec(dllimport)

+#define DLLEXPORT __declspec(dllexport)

+

+#ifdef DEBUG

+

+    #define NAME(x) TEXT(x)

+

+    // These are used internally by the debug library (PRIVATE)

+

+    void WINAPI DbgInitKeyLevels(HKEY hKey, bool fTakeMax);

+    void WINAPI DbgInitGlobalSettings(bool fTakeMax);

+    void WINAPI DbgInitModuleSettings(bool fTakeMax);

+    void WINAPI DbgInitModuleName();

+    DWORD WINAPI DbgRegisterObjectCreation(

+        LPCSTR szObjectName, LPCWSTR wszObjectName);

+

+    BOOL WINAPI DbgRegisterObjectDestruction(DWORD dwCookie);

+

+    // These are the PUBLIC entry points

+

+    BOOL WINAPI DbgCheckModuleLevel(DWORD Type,DWORD Level);

+    void WINAPI DbgSetModuleLevel(DWORD Type,DWORD Level);

+    void WINAPI DbgSetAutoRefreshLevels(bool fAuto);

+

+    // Initialise the library with the module handle

+

+    void WINAPI DbgInitialise(HINSTANCE hInst);

+    void WINAPI DbgTerminate();

+

+    void WINAPI DbgDumpObjectRegister();

+

+    // Display error and logging to the user

+

+    void WINAPI DbgAssert(LPCTSTR pCondition,LPCTSTR pFileName,INT iLine);

+    void WINAPI DbgBreakPoint(LPCTSTR pCondition,LPCTSTR pFileName,INT iLine);

+    void WINAPI DbgBreakPoint(LPCTSTR pFileName,INT iLine,__format_string LPCTSTR  szFormatString,...);

+

+    void WINAPI DbgKernelAssert(LPCTSTR pCondition,LPCTSTR pFileName,INT iLine);

+    void WINAPI DbgLogInfo(DWORD Type,DWORD Level,__format_string LPCTSTR pFormat,...);

+#ifdef UNICODE

+    void WINAPI DbgLogInfo(DWORD Type,DWORD Level,__format_string LPCSTR pFormat,...);

+    void WINAPI DbgAssert(LPCSTR pCondition,LPCSTR pFileName,INT iLine);

+    void WINAPI DbgBreakPoint(LPCSTR pCondition,LPCSTR pFileName,INT iLine);

+    void WINAPI DbgKernelAssert(LPCSTR pCondition,LPCSTR pFileName,INT iLine);

+#endif

+    void WINAPI DbgOutString(LPCTSTR psz);

+

+    //  Debug infinite wait stuff

+    DWORD WINAPI DbgWaitForSingleObject(HANDLE h);

+    DWORD WINAPI DbgWaitForMultipleObjects(DWORD nCount,

+                                    __in_ecount(nCount) CONST HANDLE *lpHandles,

+                                    BOOL bWaitAll);

+    void WINAPI DbgSetWaitTimeout(DWORD dwTimeout);

+

+#ifdef __strmif_h__

+    // Display a media type: Terse at level 2, verbose at level 5

+    void WINAPI DisplayType(LPCTSTR label, const AM_MEDIA_TYPE *pmtIn);

+

+    // Dump lots of information about a filter graph

+    void WINAPI DumpGraph(IFilterGraph *pGraph, DWORD dwLevel);

+#endif

+

+    #define KASSERT(_x_) if (!(_x_))         \

+        DbgKernelAssert(TEXT(#_x_),TEXT(__FILE__),__LINE__)

+

+    //  Break on the debugger without putting up a message box

+    //  message goes to debugger instead

+

+    #define KDbgBreak(_x_)                   \

+        DbgKernelAssert(TEXT(#_x_),TEXT(__FILE__),__LINE__)

+

+    // We chose a common name for our ASSERT macro, MFC also uses this name

+    // So long as the implementation evaluates the condition and handles it

+    // then we will be ok. Rather than override the behaviour expected we

+    // will leave whatever first defines ASSERT as the handler (i.e. MFC)

+    #ifndef ASSERT

+        #define ASSERT(_x_) if (!(_x_))         \

+            DbgAssert(TEXT(#_x_),TEXT(__FILE__),__LINE__)

+    #endif

+

+    #define DbgAssertAligned( _ptr_, _alignment_ ) ASSERT( ((DWORD_PTR) (_ptr_)) % (_alignment_) == 0)

+

+    //  Put up a message box informing the user of a halt

+    //  condition in the program

+

+    #define DbgBreak(_x_)                   \

+        DbgBreakPoint(TEXT(#_x_),TEXT(__FILE__),__LINE__)

+

+    #define EXECUTE_ASSERT(_x_) ASSERT(_x_)

+    #define DbgLog(_x_) DbgLogInfo _x_

+    // MFC style trace macros

+

+    #define NOTE(_x_)             DbgLog((LOG_TRACE,5,TEXT(_x_)))

+    #define NOTE1(_x_,a)          DbgLog((LOG_TRACE,5,TEXT(_x_),a))

+    #define NOTE2(_x_,a,b)        DbgLog((LOG_TRACE,5,TEXT(_x_),a,b))

+    #define NOTE3(_x_,a,b,c)      DbgLog((LOG_TRACE,5,TEXT(_x_),a,b,c))

+    #define NOTE4(_x_,a,b,c,d)    DbgLog((LOG_TRACE,5,TEXT(_x_),a,b,c,d))

+    #define NOTE5(_x_,a,b,c,d,e)  DbgLog((LOG_TRACE,5,TEXT(_x_),a,b,c,d,e))

+

+#else

+

+    // Retail builds make public debug functions inert  - WARNING the source

+    // files do not define or build any of the entry points in debug builds

+    // (public entry points compile to nothing) so if you go trying to call

+    // any of the private entry points in your source they won't compile

+

+    #define NAME(_x_) ((LPTSTR) NULL)

+

+    #define DbgInitialise(hInst)

+    #define DbgTerminate()

+    #define DbgLog(_x_) 0

+    #define DbgOutString(psz)

+    #define DbgAssertAligned( _ptr_, _alignment_ ) 0

+

+    #define DbgRegisterObjectCreation(pObjectName)

+    #define DbgRegisterObjectDestruction(dwCookie)

+    #define DbgDumpObjectRegister()

+

+    #define DbgCheckModuleLevel(Type,Level)

+    #define DbgSetModuleLevel(Type,Level)

+    #define DbgSetAutoRefreshLevels(fAuto)

+

+    #define DbgWaitForSingleObject(h)  WaitForSingleObject(h, INFINITE)

+    #define DbgWaitForMultipleObjects(nCount, lpHandles, bWaitAll)     \

+               WaitForMultipleObjects(nCount, lpHandles, bWaitAll, INFINITE)

+    #define DbgSetWaitTimeout(dwTimeout)

+

+    #define KDbgBreak(_x_)

+    #define DbgBreak(_x_)

+

+    #define KASSERT(_x_) ((void)0)

+    #ifndef ASSERT

+	#define ASSERT(_x_) ((void)0)

+    #endif

+    #define EXECUTE_ASSERT(_x_) ((void)(_x_))

+

+    // MFC style trace macros

+

+    #define NOTE(_x_) ((void)0)

+    #define NOTE1(_x_,a) ((void)0)

+    #define NOTE2(_x_,a,b) ((void)0)

+    #define NOTE3(_x_,a,b,c) ((void)0)

+    #define NOTE4(_x_,a,b,c,d) ((void)0)

+    #define NOTE5(_x_,a,b,c,d,e) ((void)0)

+

+    #define DisplayType(label, pmtIn) ((void)0)

+    #define DumpGraph(pGraph, label) ((void)0)

+#endif

+

+

+// Checks a pointer which should be non NULL - can be used as follows.

+

+#define CheckPointer(p,ret) {if((p)==NULL) return (ret);}

+

+//   HRESULT Foo(VOID *pBar)

+//   {

+//       CheckPointer(pBar,E_INVALIDARG)

+//   }

+//

+//   Or if the function returns a boolean

+//

+//   BOOL Foo(VOID *pBar)

+//   {

+//       CheckPointer(pBar,FALSE)

+//   }

+

+#define ValidateReadPtr(p,cb) 0

+#define ValidateWritePtr(p,cb) 0

+#define ValidateReadWritePtr(p,cb) 0

+#define ValidateStringPtr(p) 0

+#define ValidateStringPtrA(p) 0

+#define ValidateStringPtrW(p) 0

+

+

+#ifdef _OBJBASE_H_

+

+    //  Outputting GUID names.  If you want to include the name

+    //  associated with a GUID (eg CLSID_...) then

+    //

+    //      GuidNames[yourGUID]

+    //

+    //  Returns the name defined in uuids.h as a string

+

+    typedef struct {

+        CHAR   *szName;

+        GUID    guid;

+    } GUID_STRING_ENTRY;

+

+    class CGuidNameList {

+    public:

+        CHAR *operator [] (const GUID& guid);

+    };

+

+    extern CGuidNameList GuidNames;

+

+#endif

+

+#ifndef REMIND

+    //  REMIND macro - generates warning as reminder to complete coding

+    //  (eg) usage:

+    //

+    //  #pragma message (REMIND("Add automation support"))

+

+

+    #define QUOTE(x) #x

+    #define QQUOTE(y) QUOTE(y)

+    #define REMIND(str) __FILE__ "(" QQUOTE(__LINE__) ") :  " str

+#endif

+

+//  Method to display objects in a useful format

+//

+//  eg If you want to display a LONGLONG ll in a debug string do (eg)

+//

+//  DbgLog((LOG_TRACE, n, TEXT("Value is %s"), (LPCTSTR)CDisp(ll, CDISP_HEX)));

+

+

+class CDispBasic

+{

+public:

+    CDispBasic() { m_pString = m_String; };

+    ~CDispBasic();

+protected:

+    PTCHAR m_pString;  // normally points to m_String... unless too much data

+    TCHAR m_String[50];

+};

+class CDisp : public CDispBasic

+{

+public:

+    CDisp(LONGLONG ll, int Format = CDISP_HEX); // Display a LONGLONG in CDISP_HEX or CDISP_DEC form

+    CDisp(REFCLSID clsid);      // Display a GUID

+    CDisp(double d);            // Display a floating point number

+#ifdef __strmif_h__

+#ifdef __STREAMS__

+    CDisp(CRefTime t);          // Display a Reference Time

+#endif

+    CDisp(IPin *pPin);          // Display a pin as {filter clsid}(pin name)

+    CDisp(IUnknown *pUnk);      // Display a filter or pin

+#endif // __strmif_h__

+    ~CDisp();

+

+    //  Implement cast to (LPCTSTR) as parameter to logger

+    operator LPCTSTR()

+    {

+        return (LPCTSTR)m_pString;

+    };

+};

+

+

+#if defined(DEBUG)

+class CAutoTrace

+{

+private:

+    LPCTSTR  _szBlkName;

+    const int _level;

+    static const TCHAR _szEntering[];

+    static const TCHAR _szLeaving[];

+public:

+    CAutoTrace(LPCTSTR szBlkName, const int level = 15)

+        : _szBlkName(szBlkName), _level(level)

+    {DbgLog((LOG_TRACE, _level, _szEntering, _szBlkName));}

+

+    ~CAutoTrace()

+    {DbgLog((LOG_TRACE, _level, _szLeaving, _szBlkName));}

+};

+

+#if defined (__FUNCTION__)

+

+#define AMTRACEFN()  CAutoTrace __trace(TEXT(__FUNCTION__))

+#define AMTRACE(_x_) CAutoTrace __trace(TEXT(__FUNCTION__))

+

+#else

+

+#define AMTRACE(_x_) CAutoTrace __trace _x_

+#define AMTRACEFN()

+

+#endif

+

+#else

+

+#define AMTRACE(_x_)

+#define AMTRACEFN()

+

+#endif

+

+#endif // __WXDEBUG__

+

+

diff --git a/jni/pjproject-android/third_party/BaseClasses/wxlist.cpp b/jni/pjproject-android/third_party/BaseClasses/wxlist.cpp
new file mode 100644
index 0000000..df22839
--- /dev/null
+++ b/jni/pjproject-android/third_party/BaseClasses/wxlist.cpp
@@ -0,0 +1,896 @@
+//------------------------------------------------------------------------------

+// File: WXList.cpp

+//

+// Desc: DirectShow base classes - implements a non-MFC based generic list

+//       template class.

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

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

+

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

+

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

+

+/* A generic list of pointers to objects.

+   Objectives: avoid using MFC libraries in ndm kernel mode and

+   provide a really useful list type.

+

+   The class is thread safe in that separate threads may add and

+   delete items in the list concurrently although the application

+   must ensure that constructor and destructor access is suitably

+   synchronised.

+

+   The list name must not conflict with MFC classes as an

+   application may use both

+

+   The nodes form a doubly linked, NULL terminated chain with an anchor

+   block (the list object per se) holding pointers to the first and last

+   nodes and a count of the nodes.

+   There is a node cache to reduce the allocation and freeing overhead.

+   It optionally (determined at construction time) has an Event which is

+   set whenever the list becomes non-empty and reset whenever it becomes

+   empty.

+   It optionally (determined at construction time) has a Critical Section

+   which is entered during the important part of each operation.  (About

+   all you can do outside it is some parameter checking).

+

+   The node cache is a repository of nodes that are NOT in the list to speed

+   up storage allocation.  Each list has its own cache to reduce locking and

+   serialising.  The list accesses are serialised anyway for a given list - a

+   common cache would mean that we would have to separately serialise access

+   of all lists within the cache.  Because the cache only stores nodes that are

+   not in the list, releasing the cache does not release any list nodes.  This

+   means that list nodes can be copied or rechained from one list to another

+   without danger of creating a dangling reference if the original cache goes

+   away.

+

+   Questionable design decisions:

+   1. Retaining the warts for compatibility

+   2. Keeping an element count -i.e. counting whenever we do anything

+      instead of only when we want the count.

+   3. Making the chain pointers NULL terminated.  If the list object

+      itself looks just like a node and the list is kept as a ring then

+      it reduces the number of special cases.  All inserts look the same.

+*/

+

+

+#include <streams.h>

+

+/* set cursor to the position of each element of list in turn  */

+#define INTERNALTRAVERSELIST(list, cursor)               \

+for ( cursor = (list).GetHeadPositionI()           \

+    ; cursor!=NULL                               \

+    ; cursor = (list).Next(cursor)                \

+    )

+

+

+/* set cursor to the position of each element of list in turn

+   in reverse order

+*/

+#define INTERNALREVERSETRAVERSELIST(list, cursor)        \

+for ( cursor = (list).GetTailPositionI()           \

+    ; cursor!=NULL                               \

+    ; cursor = (list).Prev(cursor)                \

+    )

+

+/* Constructor calls a separate initialisation function that

+   creates a node cache, optionally creates a lock object

+   and optionally creates a signaling object.

+

+   By default we create a locking object, a DEFAULTCACHE sized

+   cache but no event object so the list cannot be used in calls

+   to WaitForSingleObject

+*/

+CBaseList::CBaseList(__in_opt LPCTSTR pName,    // Descriptive list name

+                     INT iItems) :    // Node cache size

+#ifdef DEBUG

+    CBaseObject(pName),

+#endif

+    m_pFirst(NULL),

+    m_pLast(NULL),

+    m_Count(0),

+    m_Cache(iItems)

+{

+} // constructor

+

+CBaseList::CBaseList(__in_opt LPCTSTR pName) :  // Descriptive list name

+#ifdef DEBUG

+    CBaseObject(pName),

+#endif

+    m_pFirst(NULL),

+    m_pLast(NULL),

+    m_Count(0),

+    m_Cache(DEFAULTCACHE)

+{

+} // constructor

+

+#ifdef UNICODE

+CBaseList::CBaseList(__in_opt LPCSTR pName,    // Descriptive list name

+                     INT iItems) :    // Node cache size

+#ifdef DEBUG

+    CBaseObject(pName),

+#endif

+    m_pFirst(NULL),

+    m_pLast(NULL),

+    m_Count(0),

+    m_Cache(iItems)

+{

+} // constructor

+

+CBaseList::CBaseList(__in_opt LPCSTR pName) :  // Descriptive list name

+#ifdef DEBUG

+    CBaseObject(pName),

+#endif

+    m_pFirst(NULL),

+    m_pLast(NULL),

+    m_Count(0),

+    m_Cache(DEFAULTCACHE)

+{

+} // constructor

+

+#endif

+

+/* The destructor enumerates all the node objects in the list and

+   in the cache deleting each in turn. We do not do any processing

+   on the objects that the list holds (i.e. points to) so if they

+   represent interfaces for example the creator of the list should

+   ensure that each of them is released before deleting us

+*/

+CBaseList::~CBaseList()

+{

+    /* Delete all our list nodes */

+

+    RemoveAll();

+

+} // destructor

+

+/* Remove all the nodes from the list but don't do anything

+   with the objects that each node looks after (this is the

+   responsibility of the creator).

+   Aa a last act we reset the signalling event

+   (if available) to indicate to clients that the list

+   does not have any entries in it.

+*/

+void CBaseList::RemoveAll()

+{

+    /* Free up all the CNode objects NOTE we don't bother putting the

+       deleted nodes into the cache as this method is only really called

+       in serious times of change such as when we are being deleted at

+       which point the cache will be deleted anway */

+

+    CNode *pn = m_pFirst;

+    while (pn) {

+        CNode *op = pn;

+        pn = pn->Next();

+        delete op;

+    }

+

+    /* Reset the object count and the list pointers */

+

+    m_Count = 0;

+    m_pFirst = m_pLast = NULL;

+

+} // RemoveAll

+

+

+

+/* Return a position enumerator for the entire list.

+   A position enumerator is a pointer to a node object cast to a

+   transparent type so all we do is return the head/tail node

+   pointer in the list.

+   WARNING because the position is a pointer to a node there is

+   an implicit assumption for users a the list class that after

+   deleting an object from the list that any other position

+   enumerators that you have may be invalid (since the node

+   may be gone).

+*/

+__out_opt POSITION CBaseList::GetHeadPositionI() const

+{

+    return (POSITION) m_pFirst;

+} // GetHeadPosition

+

+

+

+__out_opt POSITION CBaseList::GetTailPositionI() const

+{

+    return (POSITION) m_pLast;

+} // GetTailPosition

+

+

+

+/* Get the number of objects in the list,

+   Get the lock before accessing the count.

+   Locking may not be entirely necessary but it has the side effect

+   of making sure that all operations are complete before we get it.

+   So for example if a list is being added to this list then that

+   will have completed in full before we continue rather than seeing

+   an intermediate albeit valid state

+*/

+int CBaseList::GetCountI() const

+{

+    return m_Count;

+} // GetCount

+

+

+

+/* Return the object at rp, update rp to the next object from

+   the list or NULL if you have moved over the last object.

+   You may still call this function once we return NULL but

+   we will continue to return a NULL position value

+*/

+__out void *CBaseList::GetNextI(__inout POSITION& rp) const

+{

+    /* have we reached the end of the list */

+

+    if (rp == NULL) {

+        return NULL;

+    }

+

+    /* Lock the object before continuing */

+

+    void *pObject;

+

+    /* Copy the original position then step on */

+

+    CNode *pn = (CNode *) rp;

+    ASSERT(pn != NULL);

+    rp = (POSITION) pn->Next();

+

+    /* Get the object at the original position from the list */

+

+    pObject = pn->GetData();

+    // ASSERT(pObject != NULL);    // NULL pointers in the list are allowed.

+    return pObject;

+} //GetNext

+

+

+

+/* Return the object at p.

+   Asking for the object at NULL ASSERTs then returns NULL

+   The object is NOT locked.  The list is not being changed

+   in any way.  If another thread is busy deleting the object

+   then locking would only result in a change from one bad

+   behaviour to another.

+*/

+__out_opt void *CBaseList::GetI(__in_opt POSITION p) const

+{

+    if (p == NULL) {

+        return NULL;

+    }

+

+    CNode * pn = (CNode *) p;

+    void *pObject = pn->GetData();

+    // ASSERT(pObject != NULL);    // NULL pointers in the list are allowed.

+    return pObject;

+} //Get

+

+__out void *CBaseList::GetValidI(__in POSITION p) const

+{

+    CNode * pn = (CNode *) p;

+    void *pObject = pn->GetData();

+    // ASSERT(pObject != NULL);    // NULL pointers in the list are allowed.

+    return pObject;

+} //Get

+

+

+/* Return the first position in the list which holds the given pointer.

+   Return NULL if it's not found.

+*/

+__out_opt POSITION CBaseList::FindI( __in void * pObj) const

+{

+    POSITION pn;

+    INTERNALTRAVERSELIST(*this, pn){

+        if (GetI(pn)==pObj) {

+            return pn;

+        }

+    }

+    return NULL;

+} // Find

+

+

+

+/* Remove the first node in the list (deletes the pointer to its object

+   from the list, does not free the object itself).

+   Return the pointer to its object or NULL if empty

+*/

+__out_opt void *CBaseList::RemoveHeadI()

+{

+    /* All we do is get the head position and ask for that to be deleted.

+       We could special case this since some of the code path checking

+       in Remove() is redundant as we know there is no previous

+       node for example but it seems to gain little over the

+       added complexity

+    */

+

+    return RemoveI((POSITION)m_pFirst);

+} // RemoveHead

+

+

+

+/* Remove the last node in the list (deletes the pointer to its object

+   from the list, does not free the object itself).

+   Return the pointer to its object or NULL if empty

+*/

+__out_opt void *CBaseList::RemoveTailI()

+{

+    /* All we do is get the tail position and ask for that to be deleted.

+       We could special case this since some of the code path checking

+       in Remove() is redundant as we know there is no previous

+       node for example but it seems to gain little over the

+       added complexity

+    */

+

+    return RemoveI((POSITION)m_pLast);

+} // RemoveTail

+

+

+

+/* Remove the pointer to the object in this position from the list.

+   Deal with all the chain pointers

+   Return a pointer to the object removed from the list.

+   The node object that is freed as a result

+   of this operation is added to the node cache where

+   it can be used again.

+   Remove(NULL) is a harmless no-op - but probably is a wart.

+*/

+__out_opt void *CBaseList::RemoveI(__in_opt POSITION pos)

+{

+    /* Lock the critical section before continuing */

+

+    // ASSERT (pos!=NULL);     // Removing NULL is to be harmless!

+    if (pos==NULL) return NULL;

+

+

+    CNode *pCurrent = (CNode *) pos;

+    ASSERT(pCurrent != NULL);

+

+    /* Update the previous node */

+

+    CNode *pNode = pCurrent->Prev();

+    if (pNode == NULL) {

+        m_pFirst = pCurrent->Next();

+    } else {

+        pNode->SetNext(pCurrent->Next());

+    }

+

+    /* Update the following node */

+

+    pNode = pCurrent->Next();

+    if (pNode == NULL) {

+        m_pLast = pCurrent->Prev();

+    } else {

+        pNode->SetPrev(pCurrent->Prev());

+    }

+

+    /* Get the object this node was looking after */

+

+    void *pObject = pCurrent->GetData();

+

+    // ASSERT(pObject != NULL);    // NULL pointers in the list are allowed.

+

+    /* Try and add the node object to the cache -

+       a NULL return code from the cache means we ran out of room.

+       The cache size is fixed by a constructor argument when the

+       list is created and defaults to DEFAULTCACHE.

+       This means that the cache will have room for this many

+       node objects. So if you have a list of media samples

+       and you know there will never be more than five active at

+       any given time of them for example then override the default

+       constructor

+    */

+

+    m_Cache.AddToCache(pCurrent);

+

+    /* If the list is empty then reset the list event */

+

+    --m_Count;

+    ASSERT(m_Count >= 0);

+    return pObject;

+} // Remove

+

+

+

+/* Add this object to the tail end of our list

+   Return the new tail position.

+*/

+

+__out_opt POSITION CBaseList::AddTailI(__in void *pObject)

+{

+    /* Lock the critical section before continuing */

+

+    CNode *pNode;

+    // ASSERT(pObject);   // NULL pointers in the list are allowed.

+

+    /* If there is a node objects in the cache then use

+       that otherwise we will have to create a new one */

+

+    pNode = (CNode *) m_Cache.RemoveFromCache();

+    if (pNode == NULL) {

+        pNode = new CNode;

+    }

+

+    /* Check we have a valid object */

+

+    if (pNode == NULL) {

+        return NULL;

+    }

+

+    /* Initialise all the CNode object

+       just in case it came from the cache

+    */

+

+    pNode->SetData(pObject);

+    pNode->SetNext(NULL);

+    pNode->SetPrev(m_pLast);

+

+    if (m_pLast == NULL) {

+        m_pFirst = pNode;

+    } else {

+        m_pLast->SetNext(pNode);

+    }

+

+    /* Set the new last node pointer and also increment the number

+       of list entries, the critical section is unlocked when we

+       exit the function

+    */

+

+    m_pLast = pNode;

+    ++m_Count;

+

+    return (POSITION) pNode;

+} // AddTail(object)

+

+

+

+/* Add this object to the head end of our list

+   Return the new head position.

+*/

+__out_opt POSITION CBaseList::AddHeadI(__in void *pObject)

+{

+    CNode *pNode;

+    // ASSERT(pObject);  // NULL pointers in the list are allowed.

+

+    /* If there is a node objects in the cache then use

+       that otherwise we will have to create a new one */

+

+    pNode = (CNode *) m_Cache.RemoveFromCache();

+    if (pNode == NULL) {

+        pNode = new CNode;

+    }

+

+    /* Check we have a valid object */

+

+    if (pNode == NULL) {

+        return NULL;

+    }

+

+    /* Initialise all the CNode object

+       just in case it came from the cache

+    */

+

+    pNode->SetData(pObject);

+

+    /* chain it in (set four pointers) */

+    pNode->SetPrev(NULL);

+    pNode->SetNext(m_pFirst);

+

+    if (m_pFirst == NULL) {

+        m_pLast = pNode;

+    } else {

+        m_pFirst->SetPrev(pNode);

+    }

+    m_pFirst = pNode;

+

+    ++m_Count;

+

+    return (POSITION) pNode;

+} // AddHead(object)

+

+

+

+/* Add all the elements in *pList to the tail of this list.

+   Return TRUE if it all worked, FALSE if it didn't.

+   If it fails some elements may have been added.

+*/

+BOOL CBaseList::AddTail(__in CBaseList *pList)

+{

+    /* lock the object before starting then enumerate

+       each entry in the source list and add them one by one to

+       our list (while still holding the object lock)

+       Lock the other list too.

+    */

+    POSITION pos = pList->GetHeadPositionI();

+

+    while (pos) {

+       if (NULL == AddTailI(pList->GetNextI(pos))) {

+           return FALSE;

+       }

+    }

+    return TRUE;

+} // AddTail(list)

+

+

+

+/* Add all the elements in *pList to the head of this list.

+   Return TRUE if it all worked, FALSE if it didn't.

+   If it fails some elements may have been added.

+*/

+BOOL CBaseList::AddHead(__in CBaseList *pList)

+{

+    /* lock the object before starting then enumerate

+       each entry in the source list and add them one by one to

+       our list (while still holding the object lock)

+       Lock the other list too.

+

+       To avoid reversing the list, traverse it backwards.

+    */

+

+    POSITION pos;

+

+    INTERNALREVERSETRAVERSELIST(*pList, pos) {

+        if (NULL== AddHeadI(pList->GetValidI(pos))){

+            return FALSE;

+        }

+    }

+    return TRUE;

+} // AddHead(list)

+

+

+

+/* Add the object after position p

+   p is still valid after the operation.

+   AddAfter(NULL,x) adds x to the start - same as AddHead

+   Return the position of the new object, NULL if it failed

+*/

+__out_opt POSITION  CBaseList::AddAfterI(__in_opt POSITION pos, __in void * pObj)

+{

+    if (pos==NULL)

+        return AddHeadI(pObj);

+

+    /* As someone else might be furkling with the list -

+       Lock the critical section before continuing

+    */

+    CNode *pAfter = (CNode *) pos;

+    ASSERT(pAfter != NULL);

+    if (pAfter==m_pLast)

+        return AddTailI(pObj);

+

+    /* set pnode to point to a new node, preferably from the cache */

+

+    CNode *pNode = (CNode *) m_Cache.RemoveFromCache();

+    if (pNode == NULL) {

+        pNode = new CNode;

+    }

+

+    /* Check we have a valid object */

+

+    if (pNode == NULL) {

+        return NULL;

+    }

+

+    /* Initialise all the CNode object

+       just in case it came from the cache

+    */

+

+    pNode->SetData(pObj);

+

+    /* It is to be added to the middle of the list - there is a before

+       and after node.  Chain it after pAfter, before pBefore.

+    */

+    CNode * pBefore = pAfter->Next();

+    ASSERT(pBefore != NULL);

+

+    /* chain it in (set four pointers) */

+    pNode->SetPrev(pAfter);

+    pNode->SetNext(pBefore);

+    pBefore->SetPrev(pNode);

+    pAfter->SetNext(pNode);

+

+    ++m_Count;

+

+    return (POSITION) pNode;

+

+} // AddAfter(object)

+

+

+

+BOOL CBaseList::AddAfter(__in_opt POSITION p, __in CBaseList *pList)

+{

+    POSITION pos;

+    INTERNALTRAVERSELIST(*pList, pos) {

+        /* p follows along the elements being added */

+        p = AddAfterI(p, pList->GetValidI(pos));

+        if (p==NULL) return FALSE;

+    }

+    return TRUE;

+} // AddAfter(list)

+

+

+

+/* Mirror images:

+   Add the element or list after position p.

+   p is still valid after the operation.

+   AddBefore(NULL,x) adds x to the end - same as AddTail

+*/

+__out_opt POSITION CBaseList::AddBeforeI(__in_opt POSITION pos, __in void * pObj)

+{

+    if (pos==NULL)

+        return AddTailI(pObj);

+

+    /* set pnode to point to a new node, preferably from the cache */

+

+    CNode *pBefore = (CNode *) pos;

+    ASSERT(pBefore != NULL);

+    if (pBefore==m_pFirst)

+        return AddHeadI(pObj);

+

+    CNode * pNode = (CNode *) m_Cache.RemoveFromCache();

+    if (pNode == NULL) {

+        pNode = new CNode;

+    }

+

+    /* Check we have a valid object */

+

+    if (pNode == NULL) {

+        return NULL;

+    }

+

+    /* Initialise all the CNode object

+       just in case it came from the cache

+    */

+

+    pNode->SetData(pObj);

+

+    /* It is to be added to the middle of the list - there is a before

+       and after node.  Chain it after pAfter, before pBefore.

+    */

+

+    CNode * pAfter = pBefore->Prev();

+    ASSERT(pAfter != NULL);

+

+    /* chain it in (set four pointers) */

+    pNode->SetPrev(pAfter);

+    pNode->SetNext(pBefore);

+    pBefore->SetPrev(pNode);

+    pAfter->SetNext(pNode);

+

+    ++m_Count;

+

+    return (POSITION) pNode;

+

+} // Addbefore(object)

+

+

+

+BOOL CBaseList::AddBefore(__in_opt POSITION p, __in CBaseList *pList)

+{

+    POSITION pos;

+    INTERNALREVERSETRAVERSELIST(*pList, pos) {

+        /* p follows along the elements being added */

+        p = AddBeforeI(p, pList->GetValidI(pos));

+        if (p==NULL) return FALSE;

+    }

+    return TRUE;

+} // AddBefore(list)

+

+

+

+/* Split *this after position p in *this

+   Retain as *this the tail portion of the original *this

+   Add the head portion to the tail end of *pList

+   Return TRUE if it all worked, FALSE if it didn't.

+

+   e.g.

+      foo->MoveToTail(foo->GetHeadPosition(), bar);

+          moves one element from the head of foo to the tail of bar

+      foo->MoveToTail(NULL, bar);

+          is a no-op

+      foo->MoveToTail(foo->GetTailPosition, bar);

+          concatenates foo onto the end of bar and empties foo.

+

+   A better, except excessively long name might be

+       MoveElementsFromHeadThroughPositionToOtherTail

+*/

+BOOL CBaseList::MoveToTail

+        (__in_opt POSITION pos, __in CBaseList *pList)

+{

+    /* Algorithm:

+       Note that the elements (including their order) in the concatenation

+       of *pList to the head of *this is invariant.

+       1. Count elements to be moved

+       2. Join *pList onto the head of this to make one long chain

+       3. Set first/Last pointers in *this and *pList

+       4. Break the chain at the new place

+       5. Adjust counts

+       6. Set/Reset any events

+    */

+

+    if (pos==NULL) return TRUE;  // no-op.  Eliminates special cases later.

+

+

+    /* Make cMove the number of nodes to move */

+    CNode * p = (CNode *)pos;

+    int cMove = 0;            // number of nodes to move

+    while(p!=NULL) {

+       p = p->Prev();

+       ++cMove;

+    }

+

+

+    /* Join the two chains together */

+    if (pList->m_pLast!=NULL)

+        pList->m_pLast->SetNext(m_pFirst);

+    if (m_pFirst!=NULL)

+        m_pFirst->SetPrev(pList->m_pLast);

+

+

+    /* set first and last pointers */

+    p = (CNode *)pos;

+

+    if (pList->m_pFirst==NULL)

+        pList->m_pFirst = m_pFirst;

+    m_pFirst = p->Next();

+    if (m_pFirst==NULL)

+        m_pLast = NULL;

+    pList->m_pLast = p;

+

+

+    /* Break the chain after p to create the new pieces */

+    if (m_pFirst!=NULL)

+        m_pFirst->SetPrev(NULL);

+    p->SetNext(NULL);

+

+

+    /* Adjust the counts */

+    m_Count -= cMove;

+    pList->m_Count += cMove;

+

+    return TRUE;

+

+} // MoveToTail

+

+

+

+/* Mirror image of MoveToTail:

+   Split *this before position p in *this.

+   Retain in *this the head portion of the original *this

+   Add the tail portion to the start (i.e. head) of *pList

+   Return TRUE if it all worked, FALSE if it didn't.

+

+   e.g.

+      foo->MoveToHead(foo->GetTailPosition(), bar);

+          moves one element from the tail of foo to the head of bar

+      foo->MoveToHead(NULL, bar);

+          is a no-op

+      foo->MoveToHead(foo->GetHeadPosition, bar);

+          concatenates foo onto the start of bar and empties foo.

+*/

+BOOL CBaseList::MoveToHead

+        (__in_opt POSITION pos, __in CBaseList *pList)

+{

+

+    /* See the comments on the algorithm in MoveToTail */

+

+    if (pos==NULL) return TRUE;  // no-op.  Eliminates special cases later.

+

+    /* Make cMove the number of nodes to move */

+    CNode * p = (CNode *)pos;

+    int cMove = 0;            // number of nodes to move

+    while(p!=NULL) {

+       p = p->Next();

+       ++cMove;

+    }

+

+

+    /* Join the two chains together */

+    if (pList->m_pFirst!=NULL)

+        pList->m_pFirst->SetPrev(m_pLast);

+    if (m_pLast!=NULL)

+        m_pLast->SetNext(pList->m_pFirst);

+

+

+    /* set first and last pointers */

+    p = (CNode *)pos;

+

+

+    if (pList->m_pLast==NULL)

+        pList->m_pLast = m_pLast;

+

+    m_pLast = p->Prev();

+    if (m_pLast==NULL)

+        m_pFirst = NULL;

+    pList->m_pFirst = p;

+

+

+    /* Break the chain after p to create the new pieces */

+    if (m_pLast!=NULL)

+        m_pLast->SetNext(NULL);

+    p->SetPrev(NULL);

+

+

+    /* Adjust the counts */

+    m_Count -= cMove;

+    pList->m_Count += cMove;

+

+    return TRUE;

+

+} // MoveToHead

+

+

+

+/* Reverse the order of the [pointers to] objects in *this

+*/

+void CBaseList::Reverse()

+{

+    /* algorithm:

+       The obvious booby trap is that you flip pointers around and lose

+       addressability to the node that you are going to process next.

+       The easy way to avoid this is do do one chain at a time.

+

+       Run along the forward chain,

+       For each node, set the reverse pointer to the one ahead of us.

+       The reverse chain is now a copy of the old forward chain, including

+       the NULL termination.

+

+       Run along the reverse chain (i.e. old forward chain again)

+       For each node set the forward pointer of the node ahead to point back

+       to the one we're standing on.

+       The first node needs special treatment,

+       it's new forward pointer is NULL.

+       Finally set the First/Last pointers

+

+    */

+    CNode * p;

+

+    // Yes we COULD use a traverse, but it would look funny!

+    p = m_pFirst;

+    while (p!=NULL) {

+        CNode * q;

+        q = p->Next();

+        p->SetNext(p->Prev());

+        p->SetPrev(q);

+        p = q;

+    }

+

+    p = m_pFirst;

+    m_pFirst = m_pLast;

+    m_pLast = p;

+

+

+#if 0     // old version

+

+    if (m_pFirst==NULL) return;          // empty list

+    if (m_pFirst->Next()==NULL) return;  // single node list

+

+

+    /* run along forward chain */

+    for ( p = m_pFirst

+        ; p!=NULL

+        ; p = p->Next()

+        ){

+        p->SetPrev(p->Next());

+    }

+

+

+    /* special case first element */

+    m_pFirst->SetNext(NULL);     // fix the old first element

+

+

+    /* run along new reverse chain i.e. old forward chain again */

+    for ( p = m_pFirst           // start at the old first element

+        ; p->Prev()!=NULL        // while there's a node still to be set

+        ; p = p->Prev()          // work in the same direction as before

+        ){

+        p->Prev()->SetNext(p);

+    }

+

+

+    /* fix forward and reverse pointers

+       - the triple XOR swap would work but all the casts look hideous */

+    p = m_pFirst;

+    m_pFirst = m_pLast;

+    m_pLast = p;

+#endif

+

+} // Reverse

+

+#endif /* PJMEDIA_VIDEO_DEV_HAS_DSHOW */

diff --git a/jni/pjproject-android/third_party/BaseClasses/wxlist.h b/jni/pjproject-android/third_party/BaseClasses/wxlist.h
new file mode 100644
index 0000000..47e7123
--- /dev/null
+++ b/jni/pjproject-android/third_party/BaseClasses/wxlist.h
@@ -0,0 +1,553 @@
+//------------------------------------------------------------------------------

+// File: WXList.h

+//

+// Desc: DirectShow base classes - defines a non-MFC generic template list

+//       class.

+//

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

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

+

+

+/* A generic list of pointers to objects.

+   No storage management or copying is done on the objects pointed to.

+   Objectives: avoid using MFC libraries in ndm kernel mode and

+   provide a really useful list type.

+

+   The class is thread safe in that separate threads may add and

+   delete items in the list concurrently although the application

+   must ensure that constructor and destructor access is suitably

+   synchronised. An application can cause deadlock with operations

+   which use two lists by simultaneously calling

+   list1->Operation(list2) and list2->Operation(list1).  So don't!

+

+   The names must not conflict with MFC classes as an application

+   may use both.

+   */

+

+#ifndef __WXLIST__

+#define __WXLIST__

+

+   /* A POSITION represents (in some fashion that's opaque) a cursor

+      on the list that can be set to identify any element.  NULL is

+      a valid value and several operations regard NULL as the position

+      "one step off the end of the list".  (In an n element list there

+      are n+1 places to insert and NULL is that "n+1-th" value).

+      The POSITION of an element in the list is only invalidated if

+      that element is deleted.  Move operations may mean that what

+      was a valid POSITION in one list is now a valid POSITION in

+      a different list.

+

+      Some operations which at first sight are illegal are allowed as

+      harmless no-ops.  For instance RemoveHead is legal on an empty

+      list and it returns NULL.  This allows an atomic way to test if

+      there is an element there, and if so, get it.  The two operations

+      AddTail and RemoveHead thus implement a MONITOR (See Hoare's paper).

+

+      Single element operations return POSITIONs, non-NULL means it worked.

+      whole list operations return a BOOL.  TRUE means it all worked.

+

+      This definition is the same as the POSITION type for MFCs, so we must

+      avoid defining it twice.

+   */

+#ifndef __AFX_H__

+struct __POSITION { int unused; };

+typedef __POSITION* POSITION;

+#endif

+

+const int DEFAULTCACHE = 10;    /* Default node object cache size */

+

+/* A class representing one node in a list.

+   Each node knows a pointer to it's adjacent nodes and also a pointer

+   to the object that it looks after.

+   All of these pointers can be retrieved or set through member functions.

+*/

+class CBaseList 

+#ifdef DEBUG

+    : public CBaseObject

+#endif

+{

+    /* Making these classes inherit from CBaseObject does nothing

+       functionally but it allows us to check there are no memory

+       leaks in debug builds. 

+    */

+

+public:

+

+#ifdef DEBUG

+    class CNode : public CBaseObject {

+#else

+    class CNode {

+#endif

+

+        CNode *m_pPrev;         /* Previous node in the list */

+        CNode *m_pNext;         /* Next node in the list */

+        void *m_pObject;      /* Pointer to the object */

+

+    public:

+

+        /* Constructor - initialise the object's pointers */

+        CNode()

+#ifdef DEBUG

+            : CBaseObject(NAME("List node"))

+#endif

+        {

+        };

+

+

+        /* Return the previous node before this one */

+        __out CNode *Prev() const { return m_pPrev; };

+

+

+        /* Return the next node after this one */

+        __out CNode *Next() const { return m_pNext; };

+

+

+        /* Set the previous node before this one */

+        void SetPrev(__in_opt CNode *p) { m_pPrev = p; };

+

+

+        /* Set the next node after this one */

+        void SetNext(__in_opt CNode *p) { m_pNext = p; };

+

+

+        /* Get the pointer to the object for this node */

+        __out void *GetData() const { return m_pObject; };

+

+

+        /* Set the pointer to the object for this node */

+        void SetData(__in void *p) { m_pObject = p; };

+    };

+

+    class CNodeCache

+    {

+    public:

+        CNodeCache(INT iCacheSize) : m_iCacheSize(iCacheSize),

+                                     m_pHead(NULL),

+                                     m_iUsed(0)

+                                     {};

+        ~CNodeCache() {

+            CNode *pNode = m_pHead;

+            while (pNode) {

+                CNode *pCurrent = pNode;

+                pNode = pNode->Next();

+                delete pCurrent;

+            }

+        };

+        void AddToCache(__inout CNode *pNode)

+        {

+            if (m_iUsed < m_iCacheSize) {

+                pNode->SetNext(m_pHead);

+                m_pHead = pNode;

+                m_iUsed++;

+            } else {

+                delete pNode;

+            }

+        };

+        CNode *RemoveFromCache()

+        {

+            CNode *pNode = m_pHead;

+            if (pNode != NULL) {

+                m_pHead = pNode->Next();

+                m_iUsed--;

+                ASSERT(m_iUsed >= 0);

+            } else {

+                ASSERT(m_iUsed == 0);

+            }

+            return pNode;

+        };

+    private:

+        INT m_iCacheSize;

+        INT m_iUsed;

+        CNode *m_pHead;

+    };

+

+protected:

+

+    CNode* m_pFirst;    /* Pointer to first node in the list */

+    CNode* m_pLast;     /* Pointer to the last node in the list */

+    LONG m_Count;       /* Number of nodes currently in the list */

+

+private:

+

+    CNodeCache m_Cache; /* Cache of unused node pointers */

+

+private:

+

+    /* These override the default copy constructor and assignment

+       operator for all list classes. They are in the private class

+       declaration section so that anybody trying to pass a list

+       object by value will generate a compile time error of

+       "cannot access the private member function". If these were

+       not here then the compiler will create default constructors

+       and assignment operators which when executed first take a

+       copy of all member variables and then during destruction

+       delete them all. This must not be done for any heap

+       allocated data.

+    */

+    CBaseList(const CBaseList &refList);

+    CBaseList &operator=(const CBaseList &refList);

+

+public:

+

+    CBaseList(__in_opt LPCTSTR pName,

+              INT iItems);

+

+    CBaseList(__in_opt LPCTSTR pName);

+#ifdef UNICODE

+    CBaseList(__in_opt LPCSTR pName,

+              INT iItems);

+

+    CBaseList(__in_opt LPCSTR pName);

+#endif

+    ~CBaseList();

+

+    /* Remove all the nodes from *this i.e. make the list empty */

+    void RemoveAll();

+

+

+    /* Return a cursor which identifies the first element of *this */

+    __out_opt POSITION GetHeadPositionI() const;

+

+

+    /* Return a cursor which identifies the last element of *this */

+    __out_opt POSITION GetTailPositionI() const;

+

+

+    /* Return the number of objects in *this */

+    int GetCountI() const;

+

+protected:

+    /* Return the pointer to the object at rp,

+       Update rp to the next node in *this

+       but make it NULL if it was at the end of *this.

+       This is a wart retained for backwards compatibility.

+       GetPrev is not implemented.

+       Use Next, Prev and Get separately.

+    */

+    __out void *GetNextI(__inout POSITION& rp) const;

+

+

+    /* Return a pointer to the object at p

+       Asking for the object at NULL will return NULL harmlessly.

+    */

+    __out_opt void *GetI(__in_opt POSITION p) const;

+    __out void *GetValidI(__in POSITION p) const;

+

+public:

+    /* return the next / prev position in *this

+       return NULL when going past the end/start.

+       Next(NULL) is same as GetHeadPosition()

+       Prev(NULL) is same as GetTailPosition()

+       An n element list therefore behaves like a n+1 element

+       cycle with NULL at the start/end.

+

+       !!WARNING!! - This handling of NULL is DIFFERENT from GetNext.

+

+       Some reasons are:

+       1. For a list of n items there are n+1 positions to insert

+          These are conveniently encoded as the n POSITIONs and NULL.

+       2. If you are keeping a list sorted (fairly common) and you

+          search forward for an element to insert before and don't

+          find it you finish up with NULL as the element before which

+          to insert.  You then want that NULL to be a valid POSITION

+          so that you can insert before it and you want that insertion

+          point to mean the (n+1)-th one that doesn't have a POSITION.

+          (symmetrically if you are working backwards through the list).

+       3. It simplifies the algebra which the methods generate.

+          e.g. AddBefore(p,x) is identical to AddAfter(Prev(p),x)

+          in ALL cases.  All the other arguments probably are reflections

+          of the algebraic point.

+    */

+    __out_opt POSITION Next(__in_opt POSITION pos) const

+    {

+        if (pos == NULL) {

+            return (POSITION) m_pFirst;

+        }

+        CNode *pn = (CNode *) pos;

+        return (POSITION) pn->Next();

+    } //Next

+

+    // See Next

+    __out_opt POSITION Prev(__in_opt POSITION pos) const

+    {

+        if (pos == NULL) {

+            return (POSITION) m_pLast;

+        }

+        CNode *pn = (CNode *) pos;

+        return (POSITION) pn->Prev();

+    } //Prev

+

+

+    /* Return the first position in *this which holds the given

+       pointer.  Return NULL if the pointer was not not found.

+    */

+protected:

+    __out_opt POSITION FindI( __in void * pObj) const;

+

+    // ??? Should there be (or even should there be only)

+    // ??? POSITION FindNextAfter(void * pObj, POSITION p)

+    // ??? And of course FindPrevBefore too.

+    // ??? List.Find(&Obj) then becomes List.FindNextAfter(&Obj, NULL)

+

+

+    /* Remove the first node in *this (deletes the pointer to its

+       object from the list, does not free the object itself).

+       Return the pointer to its object.

+       If *this was already empty it will harmlessly return NULL.

+    */

+    __out_opt void *RemoveHeadI();

+

+

+    /* Remove the last node in *this (deletes the pointer to its

+       object from the list, does not free the object itself).

+       Return the pointer to its object.

+       If *this was already empty it will harmlessly return NULL.

+    */

+    __out_opt void *RemoveTailI();

+

+

+    /* Remove the node identified by p from the list (deletes the pointer

+       to its object from the list, does not free the object itself).

+       Asking to Remove the object at NULL will harmlessly return NULL.

+       Return the pointer to the object removed.

+    */

+    __out_opt void *RemoveI(__in_opt POSITION p);

+

+    /* Add single object *pObj to become a new last element of the list.

+       Return the new tail position, NULL if it fails.

+       If you are adding a COM objects, you might want AddRef it first.

+       Other existing POSITIONs in *this are still valid

+    */

+    __out_opt POSITION AddTailI(__in void * pObj);

+public:

+

+

+    /* Add all the elements in *pList to the tail of *this.

+       This duplicates all the nodes in *pList (i.e. duplicates

+       all its pointers to objects).  It does not duplicate the objects.

+       If you are adding a list of pointers to a COM object into the list

+       it's a good idea to AddRef them all  it when you AddTail it.

+       Return TRUE if it all worked, FALSE if it didn't.

+       If it fails some elements may have been added.

+       Existing POSITIONs in *this are still valid

+

+       If you actually want to MOVE the elements, use MoveToTail instead.

+    */

+    BOOL AddTail(__in CBaseList *pList);

+

+

+    /* Mirror images of AddHead: */

+

+    /* Add single object to become a new first element of the list.

+       Return the new head position, NULL if it fails.

+       Existing POSITIONs in *this are still valid

+    */

+protected:

+    __out_opt POSITION AddHeadI(__in void * pObj);

+public:

+

+    /* Add all the elements in *pList to the head of *this.

+       Same warnings apply as for AddTail.

+       Return TRUE if it all worked, FALSE if it didn't.

+       If it fails some of the objects may have been added.

+

+       If you actually want to MOVE the elements, use MoveToHead instead.

+    */

+    BOOL AddHead(__in CBaseList *pList);

+

+

+    /* Add the object *pObj to *this after position p in *this.

+       AddAfter(NULL,x) adds x to the start - equivalent to AddHead

+       Return the position of the object added, NULL if it failed.

+       Existing POSITIONs in *this are undisturbed, including p.

+    */

+protected:

+    __out_opt POSITION AddAfterI(__in_opt POSITION p, __in void * pObj);

+public:

+

+    /* Add the list *pList to *this after position p in *this

+       AddAfter(NULL,x) adds x to the start - equivalent to AddHead

+       Return TRUE if it all worked, FALSE if it didn't.

+       If it fails, some of the objects may be added

+       Existing POSITIONs in *this are undisturbed, including p.

+    */

+    BOOL AddAfter(__in_opt POSITION p, __in CBaseList *pList);

+

+

+    /* Mirror images:

+       Add the object *pObj to this-List after position p in *this.

+       AddBefore(NULL,x) adds x to the end - equivalent to AddTail

+       Return the position of the new object, NULL if it fails

+       Existing POSITIONs in *this are undisturbed, including p.

+    */

+    protected:

+    __out_opt POSITION AddBeforeI(__in_opt POSITION p, __in void * pObj);

+    public:

+

+    /* Add the list *pList to *this before position p in *this

+       AddAfter(NULL,x) adds x to the start - equivalent to AddHead

+       Return TRUE if it all worked, FALSE if it didn't.

+       If it fails, some of the objects may be added

+       Existing POSITIONs in *this are undisturbed, including p.

+    */

+    BOOL AddBefore(__in_opt POSITION p, __in CBaseList *pList);

+

+

+    /* Note that AddAfter(p,x) is equivalent to AddBefore(Next(p),x)

+       even in cases where p is NULL or Next(p) is NULL.

+       Similarly for mirror images etc.

+       This may make it easier to argue about programs.

+    */

+

+

+

+    /* The following operations do not copy any elements.

+       They move existing blocks of elements around by switching pointers.

+       They are fairly efficient for long lists as for short lists.

+       (Alas, the Count slows things down).

+

+       They split the list into two parts.

+       One part remains as the original list, the other part

+       is appended to the second list.  There are eight possible

+       variations:

+       Split the list {after/before} a given element

+       keep the {head/tail} portion in the original list

+       append the rest to the {head/tail} of the new list.

+

+       Since After is strictly equivalent to Before Next

+       we are not in serious need of the Before/After variants.

+       That leaves only four.

+

+       If you are processing a list left to right and dumping

+       the bits that you have processed into another list as

+       you go, the Tail/Tail variant gives the most natural result.

+       If you are processing in reverse order, Head/Head is best.

+

+       By using NULL positions and empty lists judiciously either

+       of the other two can be built up in two operations.

+

+       The definition of NULL (see Next/Prev etc) means that

+       degenerate cases include

+          "move all elements to new list"

+          "Split a list into two lists"

+          "Concatenate two lists"

+          (and quite a few no-ops)

+

+       !!WARNING!! The type checking won't buy you much if you get list

+       positions muddled up - e.g. use a POSITION that's in a different

+       list and see what a mess you get!

+    */

+

+    /* Split *this after position p in *this

+       Retain as *this the tail portion of the original *this

+       Add the head portion to the tail end of *pList

+       Return TRUE if it all worked, FALSE if it didn't.

+

+       e.g.

+          foo->MoveToTail(foo->GetHeadPosition(), bar);

+              moves one element from the head of foo to the tail of bar

+          foo->MoveToTail(NULL, bar);

+              is a no-op, returns NULL

+          foo->MoveToTail(foo->GetTailPosition, bar);

+              concatenates foo onto the end of bar and empties foo.

+

+       A better, except excessively long name might be

+           MoveElementsFromHeadThroughPositionToOtherTail

+    */

+    BOOL MoveToTail(__in_opt POSITION pos, __in CBaseList *pList);

+

+

+    /* Mirror image:

+       Split *this before position p in *this.

+       Retain in *this the head portion of the original *this

+       Add the tail portion to the start (i.e. head) of *pList

+

+       e.g.

+          foo->MoveToHead(foo->GetTailPosition(), bar);

+              moves one element from the tail of foo to the head of bar

+          foo->MoveToHead(NULL, bar);

+              is a no-op, returns NULL

+          foo->MoveToHead(foo->GetHeadPosition, bar);

+              concatenates foo onto the start of bar and empties foo.

+    */

+    BOOL MoveToHead(__in_opt POSITION pos, __in CBaseList *pList);

+

+

+    /* Reverse the order of the [pointers to] objects in *this

+    */

+    void Reverse();

+

+

+    /* set cursor to the position of each element of list in turn  */

+    #define TRAVERSELIST(list, cursor)               \

+    for ( cursor = (list).GetHeadPosition()           \

+        ; cursor!=NULL                               \

+        ; cursor = (list).Next(cursor)                \

+        )

+

+

+    /* set cursor to the position of each element of list in turn

+       in reverse order

+    */

+    #define REVERSETRAVERSELIST(list, cursor)        \

+    for ( cursor = (list).GetTailPosition()           \

+        ; cursor!=NULL                               \

+        ; cursor = (list).Prev(cursor)                \

+        )

+

+}; // end of class declaration

+

+template<class OBJECT> class CGenericList : public CBaseList

+{

+public:

+    CGenericList(__in_opt LPCTSTR pName,

+                 INT iItems,

+                 BOOL bLock = TRUE,

+                 BOOL bAlert = FALSE) :

+                     CBaseList(pName, iItems) {

+        UNREFERENCED_PARAMETER(bAlert);

+        UNREFERENCED_PARAMETER(bLock);

+    };

+    CGenericList(__in_opt LPCTSTR pName) :

+                     CBaseList(pName) {

+    };

+

+    __out_opt POSITION GetHeadPosition() const { return (POSITION)m_pFirst; }

+    __out_opt POSITION GetTailPosition() const { return (POSITION)m_pLast; }

+    int GetCount() const { return m_Count; }

+

+    __out OBJECT *GetNext(__inout POSITION& rp) const { return (OBJECT *) GetNextI(rp); }

+

+    __out_opt OBJECT *Get(__in_opt POSITION p) const { return (OBJECT *) GetI(p); }

+    __out OBJECT *GetValid(__in POSITION p) const { return (OBJECT *) GetValidI(p); }

+    __out_opt OBJECT *GetHead() const  { return Get(GetHeadPosition()); }

+

+    __out_opt OBJECT *RemoveHead() { return (OBJECT *) RemoveHeadI(); }

+

+    __out_opt OBJECT *RemoveTail() { return (OBJECT *) RemoveTailI(); }

+

+    __out_opt OBJECT *Remove(__in_opt POSITION p) { return (OBJECT *) RemoveI(p); }

+    __out_opt POSITION AddBefore(__in_opt POSITION p, __in OBJECT * pObj) { return AddBeforeI(p, pObj); }

+    __out_opt POSITION AddAfter(__in_opt POSITION p, __in OBJECT * pObj)  { return AddAfterI(p, pObj); }

+    __out_opt POSITION AddHead(__in OBJECT * pObj) { return AddHeadI(pObj); }

+    __out_opt POSITION AddTail(__in OBJECT * pObj)  { return AddTailI(pObj); }

+    BOOL AddTail(__in CGenericList<OBJECT> *pList)

+            { return CBaseList::AddTail((CBaseList *) pList); }

+    BOOL AddHead(__in CGenericList<OBJECT> *pList)

+            { return CBaseList::AddHead((CBaseList *) pList); }

+    BOOL AddAfter(__in_opt POSITION p, __in CGenericList<OBJECT> *pList)

+            { return CBaseList::AddAfter(p, (CBaseList *) pList); };

+    BOOL AddBefore(__in_opt POSITION p, __in CGenericList<OBJECT> *pList)

+            { return CBaseList::AddBefore(p, (CBaseList *) pList); };

+    __out_opt POSITION Find( __in OBJECT * pObj) const { return FindI(pObj); }

+}; // end of class declaration

+

+

+

+/* These define the standard list types */

+

+typedef CGenericList<CBaseObject> CBaseObjectList;

+typedef CGenericList<IUnknown> CBaseInterfaceList;

+

+#endif /* __WXLIST__ */

+

diff --git a/jni/pjproject-android/third_party/BaseClasses/wxutil.cpp b/jni/pjproject-android/third_party/BaseClasses/wxutil.cpp
new file mode 100644
index 0000000..59474ed
--- /dev/null
+++ b/jni/pjproject-android/third_party/BaseClasses/wxutil.cpp
@@ -0,0 +1,773 @@
+//------------------------------------------------------------------------------

+// File: WXUtil.cpp

+//

+// Desc: DirectShow base classes - implements helper classes for building

+//       multimedia filters.

+//

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

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

+

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

+

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

+

+#include <streams.h>

+#define STRSAFE_NO_DEPRECATE

+#include <strsafe.h>

+

+

+// --- CAMEvent -----------------------

+CAMEvent::CAMEvent(BOOL fManualReset, __inout_opt HRESULT *phr)

+{

+    m_hEvent = CreateEvent(NULL, fManualReset, FALSE, NULL);

+    if (NULL == m_hEvent) {

+        if (NULL != phr && SUCCEEDED(*phr)) {

+            *phr = E_OUTOFMEMORY;

+        }

+    }

+}

+

+CAMEvent::CAMEvent(__inout_opt HRESULT *phr)

+{

+    m_hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);

+    if (NULL == m_hEvent) {

+        if (NULL != phr && SUCCEEDED(*phr)) {

+            *phr = E_OUTOFMEMORY;

+        }

+    }

+}

+

+CAMEvent::~CAMEvent()

+{

+    if (m_hEvent) {

+	EXECUTE_ASSERT(CloseHandle(m_hEvent));

+    }

+}

+

+

+// --- CAMMsgEvent -----------------------

+// One routine.  The rest is handled in CAMEvent

+

+CAMMsgEvent::CAMMsgEvent(__inout_opt HRESULT *phr) : CAMEvent(FALSE, phr)

+{

+}

+

+BOOL CAMMsgEvent::WaitMsg(DWORD dwTimeout)

+{

+    // wait for the event to be signalled, or for the

+    // timeout (in MS) to expire.  allow SENT messages

+    // to be processed while we wait

+    DWORD dwWait;

+    DWORD dwStartTime = 0;

+

+    // set the waiting period.

+    DWORD dwWaitTime = dwTimeout;

+

+    // the timeout will eventually run down as we iterate

+    // processing messages.  grab the start time so that

+    // we can calculate elapsed times.

+    if (dwWaitTime != INFINITE) {

+        dwStartTime = timeGetTime();

+    }

+

+    do {

+        dwWait = MsgWaitForMultipleObjects(1,&m_hEvent,FALSE, dwWaitTime, QS_SENDMESSAGE);

+        if (dwWait == WAIT_OBJECT_0 + 1) {

+	    MSG Message;

+            PeekMessage(&Message,NULL,0,0,PM_NOREMOVE);

+

+	    // If we have an explicit length of time to wait calculate

+	    // the next wake up point - which might be now.

+	    // If dwTimeout is INFINITE, it stays INFINITE

+	    if (dwWaitTime != INFINITE) {

+

+		DWORD dwElapsed = timeGetTime()-dwStartTime;

+

+		dwWaitTime =

+		    (dwElapsed >= dwTimeout)

+			? 0  // wake up with WAIT_TIMEOUT

+			: dwTimeout-dwElapsed;

+	    }

+        }

+    } while (dwWait == WAIT_OBJECT_0 + 1);

+

+    // return TRUE if we woke on the event handle,

+    //        FALSE if we timed out.

+    return (dwWait == WAIT_OBJECT_0);

+}

+

+// --- CAMThread ----------------------

+

+

+CAMThread::CAMThread(__inout_opt HRESULT *phr)

+    : m_EventSend(TRUE, phr),     // must be manual-reset for CheckRequest()

+      m_EventComplete(FALSE, phr)

+{

+    m_hThread = NULL;

+}

+

+CAMThread::~CAMThread() {

+    Close();

+}

+

+

+// when the thread starts, it calls this function. We unwrap the 'this'

+//pointer and call ThreadProc.

+DWORD WINAPI

+CAMThread::InitialThreadProc(__inout LPVOID pv)

+{

+    HRESULT hrCoInit = CAMThread::CoInitializeHelper();

+    if(FAILED(hrCoInit)) {

+        DbgLog((LOG_ERROR, 1, TEXT("CoInitializeEx failed.")));

+    }

+

+    CAMThread * pThread = (CAMThread *) pv;

+

+    HRESULT hr = pThread->ThreadProc();

+

+    if(SUCCEEDED(hrCoInit)) {

+        CoUninitialize();

+    }

+

+    return hr;

+}

+

+BOOL

+CAMThread::Create()

+{

+    DWORD threadid;

+

+    CAutoLock lock(&m_AccessLock);

+

+    if (ThreadExists()) {

+	return FALSE;

+    }

+

+    m_hThread = CreateThread(

+		    NULL,

+		    0,

+		    CAMThread::InitialThreadProc,

+		    this,

+		    0,

+		    &threadid);

+

+    if (!m_hThread) {

+	return FALSE;

+    }

+

+    return TRUE;

+}

+

+DWORD

+CAMThread::CallWorker(DWORD dwParam)

+{

+    // lock access to the worker thread for scope of this object

+    CAutoLock lock(&m_AccessLock);

+

+    if (!ThreadExists()) {

+	return (DWORD) E_FAIL;

+    }

+

+    // set the parameter

+    m_dwParam = dwParam;

+

+    // signal the worker thread

+    m_EventSend.Set();

+

+    // wait for the completion to be signalled

+    m_EventComplete.Wait();

+

+    // done - this is the thread's return value

+    return m_dwReturnVal;

+}

+

+// Wait for a request from the client

+DWORD

+CAMThread::GetRequest()

+{

+    m_EventSend.Wait();

+    return m_dwParam;

+}

+

+// is there a request?

+BOOL

+CAMThread::CheckRequest(__out_opt DWORD * pParam)

+{

+    if (!m_EventSend.Check()) {

+	return FALSE;

+    } else {

+	if (pParam) {

+	    *pParam = m_dwParam;

+	}

+	return TRUE;

+    }

+}

+

+// reply to the request

+void

+CAMThread::Reply(DWORD dw)

+{

+    m_dwReturnVal = dw;

+

+    // The request is now complete so CheckRequest should fail from

+    // now on

+    //

+    // This event should be reset BEFORE we signal the client or

+    // the client may Set it before we reset it and we'll then

+    // reset it (!)

+

+    m_EventSend.Reset();

+

+    // Tell the client we're finished

+

+    m_EventComplete.Set();

+}

+

+HRESULT CAMThread::CoInitializeHelper()

+{

+    // call CoInitializeEx and tell OLE not to create a window (this

+    // thread probably won't dispatch messages and will hang on

+    // broadcast msgs o/w).

+    //

+    // If CoInitEx is not available, threads that don't call CoCreate

+    // aren't affected. Threads that do will have to handle the

+    // failure. Perhaps we should fall back to CoInitialize and risk

+    // hanging?

+    //

+

+    // older versions of ole32.dll don't have CoInitializeEx

+

+    HRESULT hr = E_FAIL;

+    HINSTANCE hOle = GetModuleHandle(TEXT("ole32.dll"));

+    if(hOle)

+    {

+        typedef HRESULT (STDAPICALLTYPE *PCoInitializeEx)(

+            LPVOID pvReserved, DWORD dwCoInit);

+        PCoInitializeEx pCoInitializeEx =

+            (PCoInitializeEx)(GetProcAddress(hOle, "CoInitializeEx"));

+        if(pCoInitializeEx)

+        {

+            hr = (*pCoInitializeEx)(0, COINIT_DISABLE_OLE1DDE );

+        }

+    }

+    else

+    {

+        // caller must load ole32.dll

+        DbgBreak("couldn't locate ole32.dll");

+    }

+

+    return hr;

+}

+

+

+// destructor for CMsgThread  - cleans up any messages left in the

+// queue when the thread exited

+CMsgThread::~CMsgThread()

+{

+    if (m_hThread != NULL) {

+        WaitForSingleObject(m_hThread, INFINITE);

+        EXECUTE_ASSERT(CloseHandle(m_hThread));

+    }

+

+    POSITION pos = m_ThreadQueue.GetHeadPosition();

+    while (pos) {

+        CMsg * pMsg = m_ThreadQueue.GetNext(pos);

+        delete pMsg;

+    }

+    m_ThreadQueue.RemoveAll();

+

+    if (m_hSem != NULL) {

+        EXECUTE_ASSERT(CloseHandle(m_hSem));

+    }

+}

+

+BOOL

+CMsgThread::CreateThread(

+    )

+{

+    m_hSem = CreateSemaphore(NULL, 0, 0x7FFFFFFF, NULL);

+    if (m_hSem == NULL) {

+        return FALSE;

+    }

+

+    m_hThread = ::CreateThread(NULL, 0, DefaultThreadProc,

+			       (LPVOID)this, 0, &m_ThreadId);

+    return m_hThread != NULL;

+}

+

+

+// This is the threads message pump.  Here we get and dispatch messages to

+// clients thread proc until the client refuses to process a message.

+// The client returns a non-zero value to stop the message pump, this

+// value becomes the threads exit code.

+

+DWORD WINAPI

+CMsgThread::DefaultThreadProc(

+    __inout LPVOID lpParam

+    )

+{

+    CMsgThread *lpThis = (CMsgThread *)lpParam;

+    CMsg msg;

+    LRESULT lResult;

+

+    // !!!

+    CoInitialize(NULL);

+

+    // allow a derived class to handle thread startup

+    lpThis->OnThreadInit();

+

+    do {

+	lpThis->GetThreadMsg(&msg);

+	lResult = lpThis->ThreadMessageProc(msg.uMsg,msg.dwFlags,

+					    msg.lpParam, msg.pEvent);

+    } while (lResult == 0L);

+

+    // !!!

+    CoUninitialize();

+

+    return (DWORD)lResult;

+}

+

+

+// Block until the next message is placed on the list m_ThreadQueue.

+// copies the message to the message pointed to by *pmsg

+void

+CMsgThread::GetThreadMsg(__out CMsg *msg)

+{

+    CMsg * pmsg = NULL;

+

+    // keep trying until a message appears

+    while (TRUE) {

+        {

+            CAutoLock lck(&m_Lock);

+            pmsg = m_ThreadQueue.RemoveHead();

+            if (pmsg == NULL) {

+                m_lWaiting++;

+            } else {

+                break;

+            }

+        }

+        // the semaphore will be signalled when it is non-empty

+        WaitForSingleObject(m_hSem, INFINITE);

+    }

+    // copy fields to caller's CMsg

+    *msg = *pmsg;

+

+    // this CMsg was allocated by the 'new' in PutThreadMsg

+    delete pmsg;

+

+}

+

+// Helper function - convert int to WSTR

+void WINAPI IntToWstr(int i, __out_ecount(12) LPWSTR wstr)

+{

+#ifdef UNICODE

+    if (FAILED(StringCchPrintf(wstr, 12, L"%d", i))) {

+        wstr[0] = 0;

+    }

+#else

+    TCHAR temp[12];

+    if (FAILED(StringCchPrintf(temp, NUMELMS(temp), "%d", i))) {

+        wstr[0] = 0;

+    } else {

+        MultiByteToWideChar(CP_ACP, 0, temp, -1, wstr, 12);

+    }

+#endif

+} // IntToWstr

+

+

+#define MEMORY_ALIGNMENT        4

+#define MEMORY_ALIGNMENT_LOG2   2

+#define MEMORY_ALIGNMENT_MASK   MEMORY_ALIGNMENT - 1

+

+void * __stdcall memmoveInternal(void * dst, const void * src, size_t count)

+{

+    void * ret = dst;

+

+#ifdef _X86_

+    if (dst <= src || (char *)dst >= ((char *)src + count)) {

+

+        /*

+         * Non-Overlapping Buffers

+         * copy from lower addresses to higher addresses

+         */

+        _asm {

+            mov     esi,src

+            mov     edi,dst

+            mov     ecx,count

+            cld

+            mov     edx,ecx

+            and     edx,MEMORY_ALIGNMENT_MASK

+            shr     ecx,MEMORY_ALIGNMENT_LOG2

+            rep     movsd

+            or      ecx,edx

+            jz      memmove_done

+            rep     movsb

+memmove_done:

+        }

+    }

+    else {

+

+        /*

+         * Overlapping Buffers

+         * copy from higher addresses to lower addresses

+         */

+        _asm {

+            mov     esi,src

+            mov     edi,dst

+            mov     ecx,count

+            std

+            add     esi,ecx

+            add     edi,ecx

+            dec     esi

+            dec     edi

+            rep     movsb

+            cld

+        }

+    }

+#else

+    MoveMemory(dst, src, count);

+#endif

+

+    return ret;

+}

+

+HRESULT AMSafeMemMoveOffset(

+    __in_bcount(dst_size) void * dst,

+    __in size_t dst_size,

+    __in DWORD cb_dst_offset,

+    __in_bcount(src_size) const void * src,

+    __in size_t src_size,

+    __in DWORD cb_src_offset,

+    __in size_t count)

+{

+    // prevent read overruns

+    if( count + cb_src_offset < count ||   // prevent integer overflow

+        count + cb_src_offset > src_size)  // prevent read overrun

+    {

+        return E_INVALIDARG;

+    }

+

+    // prevent write overruns

+    if( count + cb_dst_offset < count ||   // prevent integer overflow

+        count + cb_dst_offset > dst_size)  // prevent write overrun

+    {

+        return E_INVALIDARG;

+    }

+

+    memmoveInternal( (BYTE *)dst+cb_dst_offset, (BYTE *)src+cb_src_offset, count);

+    return S_OK;

+}

+

+

+#ifdef DEBUG

+/******************************Public*Routine******************************\

+* Debug CCritSec helpers

+*

+* We provide debug versions of the Constructor, destructor, Lock and Unlock

+* routines.  The debug code tracks who owns each critical section by

+* maintaining a depth count.

+*

+* History:

+*

+\**************************************************************************/

+

+CCritSec::CCritSec()

+{

+    InitializeCriticalSection(&m_CritSec);

+    m_currentOwner = m_lockCount = 0;

+    m_fTrace = FALSE;

+}

+

+CCritSec::~CCritSec()

+{

+    DeleteCriticalSection(&m_CritSec);

+}

+

+void CCritSec::Lock()

+{

+    UINT tracelevel=3;

+    DWORD us = GetCurrentThreadId();

+    DWORD currentOwner = m_currentOwner;

+    if (currentOwner && (currentOwner != us)) {

+        // already owned, but not by us

+        if (m_fTrace) {

+            DbgLog((LOG_LOCKING, 2, TEXT("Thread %d about to wait for lock %x owned by %d"),

+                GetCurrentThreadId(), &m_CritSec, currentOwner));

+            tracelevel=2;

+	        // if we saw the message about waiting for the critical

+	        // section we ensure we see the message when we get the

+	        // critical section

+        }

+    }

+    EnterCriticalSection(&m_CritSec);

+    if (0 == m_lockCount++) {

+        // we now own it for the first time.  Set owner information

+        m_currentOwner = us;

+

+        if (m_fTrace) {

+            DbgLog((LOG_LOCKING, tracelevel, TEXT("Thread %d now owns lock %x"), m_currentOwner, &m_CritSec));

+        }

+    }

+}

+

+void CCritSec::Unlock() {

+    if (0 == --m_lockCount) {

+        // about to be unowned

+        if (m_fTrace) {

+            DbgLog((LOG_LOCKING, 3, TEXT("Thread %d releasing lock %x"), m_currentOwner, &m_CritSec));

+        }

+

+        m_currentOwner = 0;

+    }

+    LeaveCriticalSection(&m_CritSec);

+}

+

+void WINAPI DbgLockTrace(CCritSec * pcCrit, BOOL fTrace)

+{

+    pcCrit->m_fTrace = fTrace;

+}

+

+BOOL WINAPI CritCheckIn(CCritSec * pcCrit)

+{

+    return (GetCurrentThreadId() == pcCrit->m_currentOwner);

+}

+

+BOOL WINAPI CritCheckIn(const CCritSec * pcCrit)

+{

+    return (GetCurrentThreadId() == pcCrit->m_currentOwner);

+}

+

+BOOL WINAPI CritCheckOut(CCritSec * pcCrit)

+{

+    return (GetCurrentThreadId() != pcCrit->m_currentOwner);

+}

+

+BOOL WINAPI CritCheckOut(const CCritSec * pcCrit)

+{

+    return (GetCurrentThreadId() != pcCrit->m_currentOwner);

+}

+#endif

+

+

+STDAPI WriteBSTR(__deref_out BSTR *pstrDest, LPCWSTR szSrc)

+{

+    *pstrDest = SysAllocString( szSrc );

+    if( !(*pstrDest) ) return E_OUTOFMEMORY;

+    return NOERROR;

+}

+

+

+STDAPI FreeBSTR(__deref_in BSTR* pstr)

+{

+    if( (PVOID)*pstr == NULL ) return S_FALSE;

+    SysFreeString( *pstr );

+    return NOERROR;

+}

+

+

+// Return a wide string - allocating memory for it

+// Returns:

+//    S_OK          - no error

+//    E_POINTER     - ppszReturn == NULL

+//    E_OUTOFMEMORY - can't allocate memory for returned string

+STDAPI AMGetWideString(LPCWSTR psz, __deref_out LPWSTR *ppszReturn)

+{

+    CheckPointer(ppszReturn, E_POINTER);

+    ValidateReadWritePtr(ppszReturn, sizeof(LPWSTR));

+    *ppszReturn = NULL;

+    size_t nameLen;

+    HRESULT hr = StringCbLengthW(psz, 100000, &nameLen);

+    if (FAILED(hr)) {

+        return hr;

+    }

+    *ppszReturn = (LPWSTR)CoTaskMemAlloc(nameLen + sizeof(WCHAR));

+    if (*ppszReturn == NULL) {

+       return E_OUTOFMEMORY;

+    }

+    CopyMemory(*ppszReturn, psz, nameLen + sizeof(WCHAR));

+    return NOERROR;

+}

+

+// Waits for the HANDLE hObject.  While waiting messages sent

+// to windows on our thread by SendMessage will be processed.

+// Using this function to do waits and mutual exclusion

+// avoids some deadlocks in objects with windows.

+// Return codes are the same as for WaitForSingleObject

+DWORD WINAPI WaitDispatchingMessages(

+    HANDLE hObject,

+    DWORD dwWait,

+    HWND hwnd,

+    UINT uMsg,

+    HANDLE hEvent)

+{

+    BOOL bPeeked = FALSE;

+    DWORD dwResult;

+    DWORD dwStart = 0;

+    DWORD dwThreadPriority = THREAD_PRIORITY_HIGHEST;

+

+    static UINT uMsgId = 0;

+

+    HANDLE hObjects[2] = { hObject, hEvent };

+    if (dwWait != INFINITE && dwWait != 0) {

+        dwStart = GetTickCount();

+    }

+    for (; ; ) {

+        DWORD nCount = NULL != hEvent ? 2 : 1;

+

+        //  Minimize the chance of actually dispatching any messages

+        //  by seeing if we can lock immediately.

+        dwResult = WaitForMultipleObjects(nCount, hObjects, FALSE, 0);

+        if (dwResult < WAIT_OBJECT_0 + nCount) {

+            break;

+        }

+

+        DWORD dwTimeOut = dwWait;

+        if (dwTimeOut > 10) {

+            dwTimeOut = 10;

+        }

+        dwResult = MsgWaitForMultipleObjects(

+                             nCount,

+                             hObjects,

+                             FALSE,

+                             dwTimeOut,

+                             hwnd == NULL ? QS_SENDMESSAGE :

+                                            QS_SENDMESSAGE + QS_POSTMESSAGE);

+        if (dwResult == WAIT_OBJECT_0 + nCount ||

+            dwResult == WAIT_TIMEOUT && dwTimeOut != dwWait) {

+            MSG msg;

+            if (hwnd != NULL) {

+                while (PeekMessage(&msg, hwnd, uMsg, uMsg, PM_REMOVE)) {

+                    DispatchMessage(&msg);

+                }

+            }

+            // Do this anyway - the previous peek doesn't flush out the

+            // messages

+            PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE);

+

+            if (dwWait != INFINITE && dwWait != 0) {

+                DWORD dwNow = GetTickCount();

+

+                // Working with differences handles wrap-around

+                DWORD dwDiff = dwNow - dwStart;

+                if (dwDiff > dwWait) {

+                    dwWait = 0;

+                } else {

+                    dwWait -= dwDiff;

+                }

+                dwStart = dwNow;

+            }

+            if (!bPeeked) {

+                //  Raise our priority to prevent our message queue

+                //  building up

+                dwThreadPriority = GetThreadPriority(GetCurrentThread());

+                if (dwThreadPriority < THREAD_PRIORITY_HIGHEST) {

+                    SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST);

+                }

+                bPeeked = TRUE;

+            }

+        } else {

+            break;

+        }

+    }

+    if (bPeeked) {

+        SetThreadPriority(GetCurrentThread(), dwThreadPriority);

+        if (HIWORD(GetQueueStatus(QS_POSTMESSAGE)) & QS_POSTMESSAGE) {

+            if (uMsgId == 0) {

+                uMsgId = RegisterWindowMessage(TEXT("AMUnblock"));

+            }

+            if (uMsgId != 0) {

+                MSG msg;

+                //  Remove old ones

+                while (PeekMessage(&msg, (HWND)-1, uMsgId, uMsgId, PM_REMOVE)) {

+                }

+            }

+            PostThreadMessage(GetCurrentThreadId(), uMsgId, 0, 0);

+        }

+    }

+    return dwResult;

+}

+

+HRESULT AmGetLastErrorToHResult()

+{

+    DWORD dwLastError = GetLastError();

+    if(dwLastError != 0)

+    {

+        return HRESULT_FROM_WIN32(dwLastError);

+    }

+    else

+    {

+        return E_FAIL;

+    }

+}

+

+IUnknown* QzAtlComPtrAssign(__deref_inout_opt IUnknown** pp, __in_opt IUnknown* lp)

+{

+    if (lp != NULL)

+        lp->AddRef();

+    if (*pp)

+        (*pp)->Release();

+    *pp = lp;

+    return lp;

+}

+

+/******************************************************************************

+

+CompatibleTimeSetEvent

+

+    CompatibleTimeSetEvent() sets the TIME_KILL_SYNCHRONOUS flag before calling

+timeSetEvent() if the current operating system supports it.  TIME_KILL_SYNCHRONOUS

+is supported on Windows XP and later operating systems.

+

+Parameters:

+- The same parameters as timeSetEvent().  See timeSetEvent()'s documentation in 

+the Platform SDK for more information.

+

+Return Value:

+- The same return value as timeSetEvent().  See timeSetEvent()'s documentation in 

+the Platform SDK for more information.

+

+******************************************************************************/

+MMRESULT CompatibleTimeSetEvent( UINT uDelay, UINT uResolution, __in LPTIMECALLBACK lpTimeProc, DWORD_PTR dwUser, UINT fuEvent )

+{

+    #if WINVER >= 0x0501

+    {

+        static bool fCheckedVersion = false;

+        static bool fTimeKillSynchronousFlagAvailable = false; 

+

+        if( !fCheckedVersion ) {

+            fTimeKillSynchronousFlagAvailable = TimeKillSynchronousFlagAvailable();

+            fCheckedVersion = true;

+        }

+

+        if( fTimeKillSynchronousFlagAvailable ) {

+            fuEvent = fuEvent | TIME_KILL_SYNCHRONOUS;

+        }

+    }

+    #endif // WINVER >= 0x0501

+

+    return timeSetEvent( uDelay, uResolution, lpTimeProc, dwUser, fuEvent );

+}

+

+bool TimeKillSynchronousFlagAvailable( void )

+{

+    OSVERSIONINFO osverinfo;

+

+    osverinfo.dwOSVersionInfoSize = sizeof(osverinfo);

+

+    if( GetVersionEx( &osverinfo ) ) {

+        

+        // Windows XP's major version is 5 and its' minor version is 1.

+        // timeSetEvent() started supporting the TIME_KILL_SYNCHRONOUS flag

+        // in Windows XP.

+        if( (osverinfo.dwMajorVersion > 5) || 

+            ( (osverinfo.dwMajorVersion == 5) && (osverinfo.dwMinorVersion >= 1) ) ) {

+            return true;

+        }

+    }

+

+    return false;

+}

+

+

+#endif /* PJMEDIA_VIDEO_DEV_HAS_DSHOW */

diff --git a/jni/pjproject-android/third_party/BaseClasses/wxutil.h b/jni/pjproject-android/third_party/BaseClasses/wxutil.h
new file mode 100644
index 0000000..305974a
--- /dev/null
+++ b/jni/pjproject-android/third_party/BaseClasses/wxutil.h
@@ -0,0 +1,532 @@
+//------------------------------------------------------------------------------

+// File: WXUtil.h

+//

+// Desc: DirectShow base classes - defines helper classes and functions for

+//       building multimedia filters.

+//

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

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

+

+

+#ifndef __WXUTIL__

+#define __WXUTIL__

+

+// eliminate spurious "statement has no effect" warnings.

+#pragma warning(disable: 4705)

+

+// wrapper for whatever critical section we have

+class CCritSec {

+

+    // make copy constructor and assignment operator inaccessible

+

+    CCritSec(const CCritSec &refCritSec);

+    CCritSec &operator=(const CCritSec &refCritSec);

+

+    CRITICAL_SECTION m_CritSec;

+

+#ifdef DEBUG

+public:

+    DWORD   m_currentOwner;

+    DWORD   m_lockCount;

+    BOOL    m_fTrace;        // Trace this one

+public:

+    CCritSec();

+    ~CCritSec();

+    void Lock();

+    void Unlock();

+#else

+

+public:

+    CCritSec() {

+        InitializeCriticalSection(&m_CritSec);

+    };

+

+    ~CCritSec() {

+        DeleteCriticalSection(&m_CritSec);

+    };

+

+    void Lock() {

+        EnterCriticalSection(&m_CritSec);

+    };

+

+    void Unlock() {

+        LeaveCriticalSection(&m_CritSec);

+    };

+#endif

+};

+

+//

+// To make deadlocks easier to track it is useful to insert in the

+// code an assertion that says whether we own a critical section or

+// not.  We make the routines that do the checking globals to avoid

+// having different numbers of member functions in the debug and

+// retail class implementations of CCritSec.  In addition we provide

+// a routine that allows usage of specific critical sections to be

+// traced.  This is NOT on by default - there are far too many.

+//

+

+#ifdef DEBUG

+    BOOL WINAPI CritCheckIn(CCritSec * pcCrit);

+    BOOL WINAPI CritCheckIn(const CCritSec * pcCrit);

+    BOOL WINAPI CritCheckOut(CCritSec * pcCrit);

+    BOOL WINAPI CritCheckOut(const CCritSec * pcCrit);

+    void WINAPI DbgLockTrace(CCritSec * pcCrit, BOOL fTrace);

+#else

+    #define CritCheckIn(x) TRUE

+    #define CritCheckOut(x) TRUE

+    #define DbgLockTrace(pc, fT)

+#endif

+

+

+// locks a critical section, and unlocks it automatically

+// when the lock goes out of scope

+class CAutoLock {

+

+    // make copy constructor and assignment operator inaccessible

+

+    CAutoLock(const CAutoLock &refAutoLock);

+    CAutoLock &operator=(const CAutoLock &refAutoLock);

+

+protected:

+    CCritSec * m_pLock;

+

+public:

+    CAutoLock(CCritSec * plock)

+    {

+        m_pLock = plock;

+        m_pLock->Lock();

+    };

+

+    ~CAutoLock() {

+        m_pLock->Unlock();

+    };

+};

+

+

+

+// wrapper for event objects

+class CAMEvent

+{

+

+    // make copy constructor and assignment operator inaccessible

+

+    CAMEvent(const CAMEvent &refEvent);

+    CAMEvent &operator=(const CAMEvent &refEvent);

+

+protected:

+    HANDLE m_hEvent;

+public:

+    CAMEvent(BOOL fManualReset = FALSE, __inout_opt HRESULT *phr = NULL);

+    CAMEvent(__inout_opt HRESULT *phr);

+    ~CAMEvent();

+

+    // Cast to HANDLE - we don't support this as an lvalue

+    operator HANDLE () const { return m_hEvent; };

+

+    void Set() {EXECUTE_ASSERT(SetEvent(m_hEvent));};

+    BOOL Wait(DWORD dwTimeout = INFINITE) {

+	return (WaitForSingleObject(m_hEvent, dwTimeout) == WAIT_OBJECT_0);

+    };

+    void Reset() { ResetEvent(m_hEvent); };

+    BOOL Check() { return Wait(0); };

+};

+

+

+// wrapper for event objects that do message processing

+// This adds ONE method to the CAMEvent object to allow sent

+// messages to be processed while waiting

+

+class CAMMsgEvent : public CAMEvent

+{

+

+public:

+

+    CAMMsgEvent(__inout_opt HRESULT *phr = NULL);

+

+    // Allow SEND messages to be processed while waiting

+    BOOL WaitMsg(DWORD dwTimeout = INFINITE);

+};

+

+// old name supported for the time being

+#define CTimeoutEvent CAMEvent

+

+// support for a worker thread

+

+#ifdef AM_NOVTABLE

+// simple thread class supports creation of worker thread, synchronization

+// and communication. Can be derived to simplify parameter passing

+class AM_NOVTABLE CAMThread {

+

+    // make copy constructor and assignment operator inaccessible

+

+    CAMThread(const CAMThread &refThread);

+    CAMThread &operator=(const CAMThread &refThread);

+

+    CAMEvent m_EventSend;

+    CAMEvent m_EventComplete;

+

+    DWORD m_dwParam;

+    DWORD m_dwReturnVal;

+

+protected:

+    HANDLE m_hThread;

+

+    // thread will run this function on startup

+    // must be supplied by derived class

+    virtual DWORD ThreadProc() = 0;

+

+public:

+    CAMThread(__inout_opt HRESULT *phr = NULL);

+    virtual ~CAMThread();

+

+    CCritSec m_AccessLock;	// locks access by client threads

+    CCritSec m_WorkerLock;	// locks access to shared objects

+

+    // thread initially runs this. param is actually 'this'. function

+    // just gets this and calls ThreadProc

+    static DWORD WINAPI InitialThreadProc(__inout LPVOID pv);

+

+    // start thread running  - error if already running

+    BOOL Create();

+

+    // signal the thread, and block for a response

+    //

+    DWORD CallWorker(DWORD);

+

+    // accessor thread calls this when done with thread (having told thread

+    // to exit)

+    void Close() {

+

+        // Disable warning: Conversion from LONG to PVOID of greater size

+#pragma warning(push)

+#pragma warning(disable: 4312)

+        HANDLE hThread = (HANDLE)InterlockedExchangePointer(&m_hThread, 0);

+#pragma warning(pop)

+

+        if (hThread) {

+            WaitForSingleObject(hThread, INFINITE);

+            CloseHandle(hThread);

+        }

+    };

+

+    // ThreadExists

+    // Return TRUE if the thread exists. FALSE otherwise

+    BOOL ThreadExists(void) const

+    {

+        if (m_hThread == 0) {

+            return FALSE;

+        } else {

+            return TRUE;

+        }

+    }

+

+    // wait for the next request

+    DWORD GetRequest();

+

+    // is there a request?

+    BOOL CheckRequest(__out_opt DWORD * pParam);

+

+    // reply to the request

+    void Reply(DWORD);

+

+    // If you want to do WaitForMultipleObjects you'll need to include

+    // this handle in your wait list or you won't be responsive

+    HANDLE GetRequestHandle() const { return m_EventSend; };

+

+    // Find out what the request was

+    DWORD GetRequestParam() const { return m_dwParam; };

+

+    // call CoInitializeEx (COINIT_DISABLE_OLE1DDE) if

+    // available. S_FALSE means it's not available.

+    static HRESULT CoInitializeHelper();

+};

+#endif // AM_NOVTABLE

+

+

+// CQueue

+//

+// Implements a simple Queue ADT.  The queue contains a finite number of

+// objects, access to which is controlled by a semaphore.  The semaphore

+// is created with an initial count (N).  Each time an object is added

+// a call to WaitForSingleObject is made on the semaphore's handle.  When

+// this function returns a slot has been reserved in the queue for the new

+// object.  If no slots are available the function blocks until one becomes

+// available.  Each time an object is removed from the queue ReleaseSemaphore

+// is called on the semaphore's handle, thus freeing a slot in the queue.

+// If no objects are present in the queue the function blocks until an

+// object has been added.

+

+#define DEFAULT_QUEUESIZE   2

+

+template <class T> class CQueue {

+private:

+    HANDLE          hSemPut;        // Semaphore controlling queue "putting"

+    HANDLE          hSemGet;        // Semaphore controlling queue "getting"

+    CRITICAL_SECTION CritSect;      // Thread seriallization

+    int             nMax;           // Max objects allowed in queue

+    int             iNextPut;       // Array index of next "PutMsg"

+    int             iNextGet;       // Array index of next "GetMsg"

+    T              *QueueObjects;   // Array of objects (ptr's to void)

+

+    void Initialize(int n) {

+        iNextPut = iNextGet = 0;

+        nMax = n;

+        InitializeCriticalSection(&CritSect);

+        hSemPut = CreateSemaphore(NULL, n, n, NULL);

+        hSemGet = CreateSemaphore(NULL, 0, n, NULL);

+        QueueObjects = new T[n];

+    }

+

+

+public:

+    CQueue(int n) {

+        Initialize(n);

+    }

+

+    CQueue() {

+        Initialize(DEFAULT_QUEUESIZE);

+    }

+

+    ~CQueue() {

+        delete [] QueueObjects;

+        DeleteCriticalSection(&CritSect);

+        CloseHandle(hSemPut);

+        CloseHandle(hSemGet);

+    }

+

+    T GetQueueObject() {

+        int iSlot;

+        T Object;

+        LONG lPrevious;

+

+        // Wait for someone to put something on our queue, returns straight

+        // away is there is already an object on the queue.

+        //

+        WaitForSingleObject(hSemGet, INFINITE);

+

+        EnterCriticalSection(&CritSect);

+        iSlot = iNextGet++ % nMax;

+        Object = QueueObjects[iSlot];

+        LeaveCriticalSection(&CritSect);

+

+        // Release anyone waiting to put an object onto our queue as there

+        // is now space available in the queue.

+        //

+        ReleaseSemaphore(hSemPut, 1L, &lPrevious);

+        return Object;

+    }

+

+    void PutQueueObject(T Object) {

+        int iSlot;

+        LONG lPrevious;

+

+        // Wait for someone to get something from our queue, returns straight

+        // away is there is already an empty slot on the queue.

+        //

+        WaitForSingleObject(hSemPut, INFINITE);

+

+        EnterCriticalSection(&CritSect);

+        iSlot = iNextPut++ % nMax;

+        QueueObjects[iSlot] = Object;

+        LeaveCriticalSection(&CritSect);

+

+        // Release anyone waiting to remove an object from our queue as there

+        // is now an object available to be removed.

+        //

+        ReleaseSemaphore(hSemGet, 1L, &lPrevious);

+    }

+};

+

+// Ensures that memory is not read past the length source buffer

+// and that memory is not written past the length of the dst buffer

+//   dst - buffer to copy to

+//   dst_size - total size of destination buffer

+//   cb_dst_offset - offset, first byte copied to dst+cb_dst_offset

+//   src - buffer to copy from

+//   src_size - total size of source buffer

+//   cb_src_offset - offset, first byte copied from src+cb_src_offset

+//   count - number of bytes to copy

+//

+// Returns:

+//    S_OK          - no error

+//    E_INVALIDARG  - values passed would lead to overrun

+HRESULT AMSafeMemMoveOffset(

+    __in_bcount(dst_size) void * dst,

+    __in size_t dst_size,

+    __in DWORD cb_dst_offset,

+    __in_bcount(src_size) const void * src,

+    __in size_t src_size,

+    __in DWORD cb_src_offset,

+    __in size_t count);

+

+extern "C"

+void * __stdcall memmoveInternal(void *, const void *, size_t);

+

+inline void * __cdecl memchrInternal(const void *buf, int chr, size_t cnt)

+{

+#ifdef _X86_

+    void *pRet = NULL;

+

+    _asm {

+        cld                 // make sure we get the direction right

+        mov     ecx, cnt    // num of bytes to scan

+        mov     edi, buf    // pointer byte stream

+        mov     eax, chr    // byte to scan for

+        repne   scasb       // look for the byte in the byte stream

+        jnz     exit_memchr // Z flag set if byte found

+        dec     edi         // scasb always increments edi even when it

+                            // finds the required byte

+        mov     pRet, edi

+exit_memchr:

+    }

+    return pRet;

+

+#else

+    while ( cnt && (*(unsigned char *)buf != (unsigned char)chr) ) {

+        buf = (unsigned char *)buf + 1;

+        cnt--;

+    }

+

+    return(cnt ? (void *)buf : NULL);

+#endif

+}

+

+void WINAPI IntToWstr(int i, __out_ecount(12) LPWSTR wstr);

+

+#define WstrToInt(sz) _wtoi(sz)

+#define atoiW(sz) _wtoi(sz)

+#define atoiA(sz) atoi(sz)

+

+// These are available to help managing bitmap VIDEOINFOHEADER media structures

+

+extern const DWORD bits555[3];

+extern const DWORD bits565[3];

+extern const DWORD bits888[3];

+

+// These help convert between VIDEOINFOHEADER and BITMAPINFO structures

+

+STDAPI_(const GUID) GetTrueColorType(const BITMAPINFOHEADER *pbmiHeader);

+STDAPI_(const GUID) GetBitmapSubtype(const BITMAPINFOHEADER *pbmiHeader);

+STDAPI_(WORD) GetBitCount(const GUID *pSubtype);

+

+// strmbase.lib implements this for compatibility with people who

+// managed to link to this directly.  we don't want to advertise it.

+//

+// STDAPI_(/* T */ CHAR *) GetSubtypeName(const GUID *pSubtype);

+

+STDAPI_(CHAR *) GetSubtypeNameA(const GUID *pSubtype);

+STDAPI_(WCHAR *) GetSubtypeNameW(const GUID *pSubtype);

+

+#ifdef UNICODE

+#define GetSubtypeName GetSubtypeNameW

+#else

+#define GetSubtypeName GetSubtypeNameA

+#endif

+

+STDAPI_(LONG) GetBitmapFormatSize(const BITMAPINFOHEADER *pHeader);

+STDAPI_(DWORD) GetBitmapSize(const BITMAPINFOHEADER *pHeader);

+

+#ifdef __AMVIDEO__

+STDAPI_(BOOL) ContainsPalette(const VIDEOINFOHEADER *pVideoInfo);

+STDAPI_(const RGBQUAD *) GetBitmapPalette(const VIDEOINFOHEADER *pVideoInfo);

+#endif // __AMVIDEO__

+

+

+// Compares two interfaces and returns TRUE if they are on the same object

+BOOL WINAPI IsEqualObject(IUnknown *pFirst, IUnknown *pSecond);

+

+// This is for comparing pins

+#define EqualPins(pPin1, pPin2) IsEqualObject(pPin1, pPin2)

+

+

+// Arithmetic helper functions

+

+// Compute (a * b + rnd) / c

+LONGLONG WINAPI llMulDiv(LONGLONG a, LONGLONG b, LONGLONG c, LONGLONG rnd);

+LONGLONG WINAPI Int64x32Div32(LONGLONG a, LONG b, LONG c, LONG rnd);

+

+

+// Avoids us dyna-linking to SysAllocString to copy BSTR strings

+STDAPI WriteBSTR(__deref_out BSTR * pstrDest, LPCWSTR szSrc);

+STDAPI FreeBSTR(__deref_in BSTR* pstr);

+

+// Return a wide string - allocating memory for it

+// Returns:

+//    S_OK          - no error

+//    E_POINTER     - ppszReturn == NULL

+//    E_OUTOFMEMORY - can't allocate memory for returned string

+STDAPI AMGetWideString(LPCWSTR pszString, __deref_out LPWSTR *ppszReturn);

+

+// Special wait for objects owning windows

+DWORD WINAPI WaitDispatchingMessages(

+    HANDLE hObject,

+    DWORD dwWait,

+    HWND hwnd = NULL,

+    UINT uMsg = 0,

+    HANDLE hEvent = NULL);

+

+// HRESULT_FROM_WIN32 converts ERROR_SUCCESS to a success code, but in

+// our use of HRESULT_FROM_WIN32, it typically means a function failed

+// to call SetLastError(), and we still want a failure code.

+//

+#define AmHresultFromWin32(x) (MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, x))

+

+// call GetLastError and return an HRESULT value that will fail the

+// SUCCEEDED() macro.

+HRESULT AmGetLastErrorToHResult(void);

+

+// duplicate of ATL's CComPtr to avoid linker conflicts.

+

+IUnknown* QzAtlComPtrAssign(__deref_inout_opt IUnknown** pp, __in_opt IUnknown* lp);

+

+template <class T>

+class QzCComPtr

+{

+public:

+	typedef T _PtrClass;

+	QzCComPtr() {p=NULL;}

+	QzCComPtr(T* lp)

+	{

+		if ((p = lp) != NULL)

+			p->AddRef();

+	}

+	QzCComPtr(const QzCComPtr<T>& lp)

+	{

+		if ((p = lp.p) != NULL)

+			p->AddRef();

+	}

+	~QzCComPtr() {if (p) p->Release();}

+	void Release() {if (p) p->Release(); p=NULL;}

+	operator T*() {return (T*)p;}

+	T& operator*() {ASSERT(p!=NULL); return *p; }

+	//The assert on operator& usually indicates a bug.  If this is really

+	//what is needed, however, take the address of the p member explicitly.

+	T** operator&() { ASSERT(p==NULL); return &p; }

+	T* operator->() { ASSERT(p!=NULL); return p; }

+	T* operator=(T* lp){return (T*)QzAtlComPtrAssign((IUnknown**)&p, lp);}

+	T* operator=(const QzCComPtr<T>& lp)

+	{

+		return (T*)QzAtlComPtrAssign((IUnknown**)&p, lp.p);

+	}

+#if _MSC_VER>1020

+	bool operator!(){return (p == NULL);}

+#else

+	BOOL operator!(){return (p == NULL) ? TRUE : FALSE;}

+#endif

+	T* p;

+};

+

+MMRESULT CompatibleTimeSetEvent( UINT uDelay, UINT uResolution, __in LPTIMECALLBACK lpTimeProc, DWORD_PTR dwUser, UINT fuEvent );

+bool TimeKillSynchronousFlagAvailable( void );

+

+//  Helper to replace lstrcpmi

+__inline int lstrcmpiLocaleIndependentW(LPCWSTR lpsz1, LPCWSTR lpsz2)

+{

+    return  CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, lpsz1, -1, lpsz2, -1) - CSTR_EQUAL;

+}

+__inline int lstrcmpiLocaleIndependentA(LPCSTR lpsz1, LPCSTR lpsz2)

+{

+    return  CompareStringA(LOCALE_INVARIANT, NORM_IGNORECASE, lpsz1, -1, lpsz2, -1) - CSTR_EQUAL;

+}

+

+#endif /* __WXUTIL__ */