amfilter.cpp
资源名称:p2p_vod.rar [点击查看]
上传用户:liguizhu
上传日期:2015-11-01
资源大小:2422k
文件大小:149k
源码类别:
P2P编程
开发平台:
Visual C++
- //------------------------------------------------------------------------------
- // File: AMFilter.cpp
- //
- // Desc: DirectShow base classes - implements class hierarchy for streams
- // architecture.
- //
- // Copyright (c) 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>
- //=====================================================================
- // Helpers
- //=====================================================================
- STDAPI CreateMemoryAllocator(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(
- LPUNKNOWN pAgg,
- BOOL bRenderer,
- IPin *pPin,
- 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(const TCHAR *pName,
- LPUNKNOWN pUnk,
- 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,
- 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(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, 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(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(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,
- 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(const TCHAR *pName,
- LPUNKNOWN pUnk,
- 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)
- {
- ASSERT(pLock != NULL);
- }
- /* Passes in a redundant HRESULT argument */
- CBaseFilter::CBaseFilter(TCHAR *pName,
- LPUNKNOWN pUnk,
- CCritSec *pLock,
- REFCLSID clsid,
- 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)
- {
- ASSERT(pLock != NULL);
- UNREFERENCED_PARAMETER(phr);
- }
- #ifdef UNICODE
- CBaseFilter::CBaseFilter(const CHAR *pName,
- LPUNKNOWN pUnk,
- 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)
- {
- ASSERT(pLock != NULL);
- }
- CBaseFilter::CBaseFilter(CHAR *pName,
- LPUNKNOWN pUnk,
- CCritSec *pLock,
- REFCLSID clsid,
- 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)
- {
- ASSERT(pLock != NULL);
- UNREFERENCED_PARAMETER(phr);
- }
- #endif
- /* Destructor */
- CBaseFilter::~CBaseFilter()
- {
- // 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(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, 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(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(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);
- // 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;
- }
- }
- }
- }
- 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);
- // 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;
- }
- }
- }
- }
- 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);
- // 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;
- }
- }
- }
- }
- 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(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,
- 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);
- ASSERT(pPin != NULL);
- 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(FILTER_INFO * pInfo)
- {
- CheckPointer(pInfo,E_POINTER);
- ValidateReadWritePtr(pInfo,sizeof(FILTER_INFO));
- if (m_pName) {
- lstrcpynW(pInfo->achName, m_pName, sizeof(pInfo->achName)/sizeof(WCHAR));
- } else {
- pInfo->achName[0] = L' ';
- }
- pInfo->pGraph = m_pGraph;
- if (m_pGraph)
- m_pGraph->AddRef();
- return NOERROR;
- }
- /* Provide the filter with a filter graph */
- STDMETHODIMP
- CBaseFilter::JoinFilterGraph(
- IFilterGraph * pGraph,
- 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) {
- DWORD nameLen = lstrlenW(pName)+1;
- m_pName = new WCHAR[nameLen];
- if (m_pName) {
- CopyMemory(m_pName, pName, nameLen*sizeof(WCHAR));
- } else {
- // !!! error here?
- ASSERT(FALSE);
- }
- }
- 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(
- 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,
- 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(CBaseFilter *pFilter,
- 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,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(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...
- IPin **ppPins, // ...in this array
- 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();
- }
- /* Calculate the number of available pins */
- int cRealPins = min(m_PinCount - m_Position, (int) cPins);
- if (cRealPins == 0) {
- return S_FALSE;
- }
- /* 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 (cRealPins && (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);
- cRealPins--;
- }
- }
- 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(CBasePin *pPin,
- 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,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(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...
- AM_MEDIA_TYPE **ppMediaTypes, // ...in this array
- 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 = (AM_MEDIA_TYPE)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(TCHAR *pObjectName,
- CBaseFilter *pFilter,
- CCritSec *pLock,
- HRESULT *phr,
- 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
- */
- ASSERT(pFilter != NULL);
- ASSERT(pLock != NULL);
- if (pName) {
- DWORD nameLen = lstrlenW(pName)+1;
- m_pName = new WCHAR[nameLen];
- if (m_pName) {
- CopyMemory(m_pName, pName, nameLen*sizeof(WCHAR));
- }
- }
- #ifdef DEBUG
- m_cRef = 0;
- #endif
- }
- #ifdef UNICODE
- CBasePin::CBasePin(CHAR *pObjectName,
- CBaseFilter *pFilter,
- CCritSec *pLock,
- HRESULT *phr,
- 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
- */
- ASSERT(pFilter != NULL);
- ASSERT(pLock != NULL);
- if (pName) {
- DWORD nameLen = lstrlenW(pName)+1;
- m_pName = new WCHAR[nameLen];
- if (m_pName) {
- CopyMemory(m_pName, pName, nameLen*sizeof(WCHAR));
- }
- }
- #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()
- {
- // 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, 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))) {
- (void)StringCchCopyW(ConnectPinInfo.achName, NUMELMS(ConnectPinInfo.achName),L"Bad Pin");
- } else {
- QueryPinInfoReleaseFilter(ConnectPinInfo);
- }
- if (FAILED(pReceivePin->QueryPinInfo(&ReceivePinInfo))) {
- (void)StringCchCopyW(ReceivePinInfo.achName, NUMELMS(ReceivePinInfo.achName),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,
- 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() ) );
- return hr;
- }
- DbgLog((LOG_TRACE, CONNECT_TRACE_LEVEL, TEXT("Connection succeeded")));
- 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,
- 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 ((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;
- }
- DeleteMediaType(pMediaType);
- 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() ) );
- 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;
- }
- return hr;
- }
- /* Complete the connection */
- m_Connected = pConnector;
- m_Connected->AddRef();
- hr = SetMediaType(pcmt);
- if (SUCCEEDED(hr)) {
- hr = CompleteConnect(pConnector);
- if (SUCCEEDED(hr)) {
- 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() ) );
- 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 ) ) {
- // 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;
- return S_OK;
- } else {
- // no connection - not an error
- return S_FALSE;
- }
- }
- /* Return an AddRef()'d pointer to the connected pin if there is one */
- STDMETHODIMP
- CBasePin::ConnectedTo(
- 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(
- 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(
- 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) {
- lstrcpynW(pInfo->achName, m_pName, sizeof(pInfo->achName)/sizeof(WCHAR));
- } else {
- pInfo->achName[0] = L' ';
- }
- pInfo->dir = m_dir;
- return NOERROR;
- }
- STDMETHODIMP
- CBasePin::QueryDirection(
- 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(
- 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(
- 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, 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(TCHAR *pObjectName,
- CBaseFilter *pFilter,
- CCritSec *pLock,
- HRESULT *phr,
- LPCWSTR pName) :
- CBasePin(pObjectName, pFilter, pLock, phr, pName, PINDIR_OUTPUT),
- m_pAllocator(NULL),
- m_pInputPin(NULL)
- {
- ASSERT(pFilter);
- }
- #ifdef UNICODE
- CBaseOutputPin::CBaseOutputPin(CHAR *pObjectName,
- CBaseFilter *pFilter,
- CCritSec *pLock,
- HRESULT *phr,
- 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(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, 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(IMediaSample ** ppSample,
- REFERENCE_TIME * pStartTime,
- 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.