summaryrefslogtreecommitdiff
path: root/third_party/BaseClasses/amfilter.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/BaseClasses/amfilter.cpp')
-rw-r--r--third_party/BaseClasses/amfilter.cpp5358
1 files changed, 5358 insertions, 0 deletions
diff --git a/third_party/BaseClasses/amfilter.cpp b/third_party/BaseClasses/amfilter.cpp
new file mode 100644
index 00000000..03c13d14
--- /dev/null
+++ b/third_party/BaseClasses/amfilter.cpp
@@ -0,0 +1,5358 @@
+//------------------------------------------------------------------------------
+// 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 <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)
+