windows-nt/Source/XPSP1/NT/net/tapi/skywalker/mspbase/mspstrm.cpp
2020-09-26 16:20:57 +08:00

1038 lines
24 KiB
C++

/*++
Copyright (c) 1997-1999 Microsoft Corporation
Module Name:
mspstrm.cpp
Abstract:
This module contains implementation of CMSPStream. The object represents
one stream in the filter graph.
--*/
#include "precomp.h"
#pragma hdrstop
/////////////////////////////////////////////////////////////////////////////
// CMSPStream
/////////////////////////////////////////////////////////////////////////////
CMSPStream::CMSPStream()
: m_dwState(STRM_INITIAL),
m_dwMediaType(0),
m_pFTM(NULL),
m_hAddress(NULL),
m_pMSPCall(NULL),
m_pIGraphBuilder(NULL),
m_pIMediaControl(NULL),
m_pPTEventSink( NULL )
{
LOG((MSP_TRACE, "CMSPStream::CMSPStream - enter"));
LOG((MSP_TRACE, "CMSPStream::CMSPStream - exit"));
}
CMSPStream::~CMSPStream()
{
LOG((MSP_TRACE, "CMSPStream::~CMSPStream - enter"));
ReleaseSink();
LOG((MSP_TRACE, "CMSPStream::~CMSPStream - exit"));
}
STDMETHODIMP CMSPStream::get_MediaType(
OUT long * plTapiMediaType
)
{
LOG((MSP_TRACE, "CMSPStream::get_MediaType - enter"));
if (MSPB_IsBadWritePtr(plTapiMediaType, sizeof (long *)))
{
LOG((MSP_ERROR, "CMSPStream::get_MediaType - exit E_POINTER"));
return E_POINTER;
}
CLock lock(m_lock);
*plTapiMediaType = m_dwMediaType;
LOG((MSP_TRACE, "CMSPStream::get_MediaType - exit S_OK"));
return S_OK;
}
STDMETHODIMP CMSPStream::get_Direction(
OUT TERMINAL_DIRECTION * pTerminalDirection
)
{
LOG((MSP_TRACE, "CMSPStream::get_Direction - enter"));
if (MSPB_IsBadWritePtr(pTerminalDirection, sizeof (TERMINAL_DIRECTION *)))
{
LOG((MSP_ERROR, "CMSPStream::get_Direction - exit E_POINTER"));
return E_POINTER;
}
CLock lock(m_lock);
*pTerminalDirection = m_Direction;
LOG((MSP_TRACE, "CMSPStream::get_Direction - exit S_OK"));
return S_OK;
}
STDMETHODIMP CMSPStream::SelectTerminal(
IN ITTerminal * pTerminal
)
/*++
Routine Description:
Arguments:
Return Value:
S_OK
E_POINTER
E_OUTOFMEMORY
TAPI_E_MAXTERMINALS
TAPI_E_INVALIDTERMINAL
--*/
{
LOG((MSP_TRACE, "CMSPStream::SelectTerminal - enter"));
//
// Check parameter.
//
if ( IsBadReadPtr(pTerminal, sizeof(ITTerminal) ) )
{
LOG((MSP_ERROR, "CMSPStream::SelectTerminal - exit E_POINTER"));
return E_POINTER;
}
HRESULT hr;
ITTerminalControl *pTerminalControl;
//
// Get the private interface from this terminal.
//
hr = pTerminal->QueryInterface(IID_ITTerminalControl,
(void **) &pTerminalControl);
if (FAILED(hr))
{
LOG((MSP_ERROR, "CMSPStream::SelectTerminal - "
"can't get ITTerminalControl - exit TAPI_E_INVALIDTERMINAL"));
return TAPI_E_INVALIDTERMINAL;
}
//
// Get the address handle and release the private interface.
//
MSP_HANDLE hAddress;
hr = pTerminalControl->get_AddressHandle(&hAddress);
pTerminalControl->Release();
if (FAILED(hr))
{
LOG((MSP_ERROR, "CMSPStream::SelectTerminal - "
"can't get address handle - exit TAPI_E_INVALIDTERMINAL"));
return TAPI_E_INVALIDTERMINAL;
}
//
// Find out if the terminal belongs to this address. Reject it if it belongs
// to another address, but accept it if it is an application-provided terminal
// (NULL address handle).
//
if ( ( hAddress != NULL ) && ( hAddress != (MSP_HANDLE) m_hAddress ) )
{
LOG((MSP_ERROR, "CMSPStream::SelectTerminal - "
"terminal from another address - exit TAPI_E_INVALIDTERMINAL"));
return TAPI_E_INVALIDTERMINAL;
}
//
// Find out if the terminal is already in our list.
//
CLock lock(m_lock);
if (m_Terminals.Find(pTerminal) >= 0)
{
LOG((MSP_ERROR, "CMSPStream::SelectTerminal - "
"terminal already selected - exit TAPI_E_INVALIDTERMINAL"));
return TAPI_E_INVALIDTERMINAL;
}
//
// Add the new terminal into our list and addref it.
//
if (!m_Terminals.Add(pTerminal))
{
LOG((MSP_ERROR, "CMSPStream::SelectTerminal - "
"exit E_OUTOFMEMORY"));
return E_OUTOFMEMORY;
}
pTerminal->AddRef();
hr = RegisterPluggableTerminalEventSink( pTerminal );
if( FAILED(hr) )
{
LOG((MSP_TRACE, "CMSPStream::SelectTerminal - "
"something wrong in RegisterPluggableTerminalEventSink"));
}
LOG((MSP_TRACE, "CMSPStream::SelectTerminal - exit S_OK"));
return S_OK;
}
STDMETHODIMP CMSPStream::UnselectTerminal(
IN ITTerminal * pTerminal
)
{
LOG((MSP_TRACE, "CMSPStream::UnselectTerminal - enter"));
//
// find out if the terminal is in our list.
//
CLock lock(m_lock);
int index;
if ((index = m_Terminals.Find(pTerminal)) < 0)
{
LOG((MSP_ERROR, "CMSPStream::UnselectTerminal - "
"exit TAPI_E_INVALIDTERMINAL"));
return TAPI_E_INVALIDTERMINAL;
}
//
// Unregister the PTEventSink object
//
HRESULT hr = E_FAIL;
hr = UnregisterPluggableTerminalEventSink( pTerminal );
if( FAILED(hr) )
{
LOG((MSP_TRACE, "CMSPStream::UnselectTerminal - "
"something wrong in UnregisterPluggableTerminalEventSink"));
}
//
// remove the terminal from our list and release it.
//
if (!m_Terminals.RemoveAt(index))
{
LOG((MSP_ERROR, "CMSPStream::UnselectTerminal - "
"exit E_UNEXPECTED"));
return E_UNEXPECTED;
}
pTerminal->Release();
LOG((MSP_TRACE, "CMSPStream::UnselectTerminal - exit S_OK"));
return S_OK;
}
STDMETHODIMP CMSPStream::EnumerateTerminals(
OUT IEnumTerminal ** ppEnumTerminal
)
{
LOG((MSP_TRACE,
"EnumerateTerminals entered. ppEnumTerminal:%x", ppEnumTerminal));
if (MSPB_IsBadWritePtr(ppEnumTerminal, sizeof(VOID *)))
{
LOG((MSP_ERROR, "ppEnumTerminal is a bad pointer"));
return E_POINTER;
}
// acquire the lock before accessing the Terminal object list.
CLock lock(m_lock);
if (m_Terminals.GetData() == NULL)
{
LOG((MSP_ERROR, "CMSPStream::EnumerateTerminals - "
"stream appears to have been shut down - exit E_UNEXPECTED"));
return E_UNEXPECTED;
}
typedef _CopyInterface<ITTerminal> CCopy;
typedef CSafeComEnum<IEnumTerminal, &IID_IEnumTerminal,
ITTerminal *, CCopy> CEnumerator;
HRESULT hr;
CMSPComObject<CEnumerator> *pEnum = NULL;
hr = CMSPComObject<CEnumerator>::CreateInstance(&pEnum);
if (pEnum == NULL)
{
LOG((MSP_ERROR, "Could not create enumerator object, %x", hr));
return hr;
}
// query for the IID_IEnumTerminal i/f
hr = pEnum->_InternalQueryInterface(IID_IEnumTerminal, (void**)ppEnumTerminal);
if (FAILED(hr))
{
LOG((MSP_ERROR, "query enum interface failed, %x", hr));
delete pEnum;
return hr;
}
// The CSafeComEnum can handle zero-sized array.
hr = pEnum->Init(
m_Terminals.GetData(), // the begin itor
m_Terminals.GetData() + m_Terminals.GetSize(), // the end itor,
NULL, // IUnknown
AtlFlagCopy // copy the data.
);
if (FAILED(hr))
{
LOG((MSP_ERROR, "init enumerator object failed, %x", hr));
(*ppEnumTerminal)->Release();
return hr;
}
LOG((MSP_TRACE, "CMSPStream::EnumerateTerminals - exit S_OK"));
return hr;
}
STDMETHODIMP CMSPStream::get_Terminals(
OUT VARIANT * pVariant
)
{
LOG((MSP_TRACE, "CMSPStream::get_Terminals - enter"));
//
// Check parameters.
//
if ( MSPB_IsBadWritePtr(pVariant, sizeof(VARIANT) ) )
{
LOG((MSP_ERROR, "CMSPStream::get_Terminals - "
"bad pointer argument - exit E_POINTER"));
return E_POINTER;
}
//
// See if this stream has been shut down. Acquire the lock before accessing
// the terminal object list.
//
CLock lock(m_lock);
if (m_Terminals.GetData() == NULL)
{
LOG((MSP_ERROR, "CMSPStream::get_Terminals - "
"stream appears to have been shut down - exit E_UNEXPECTED"));
return E_UNEXPECTED;
}
//
// create the collection object - see mspcoll.h
//
HRESULT hr;
typedef CTapiIfCollection< ITTerminal * > TerminalCollection;
CComObject<TerminalCollection> * pCollection;
hr = CComObject<TerminalCollection>::CreateInstance( &pCollection );
if ( FAILED(hr) )
{
LOG((MSP_ERROR, "CMSPStream::get_Terminals - "
"can't create collection - exit 0x%08x", hr));
return hr;
}
//
// get the Collection's IDispatch interface
//
IDispatch * pDispatch;
hr = pCollection->_InternalQueryInterface(IID_IDispatch,
(void **) &pDispatch );
if ( FAILED(hr) )
{
LOG((MSP_ERROR, "CMSPStream::get_Terminals - "
"QI for IDispatch on collection failed - exit 0x%08x", hr));
delete pCollection;
return hr;
}
//
// Init the collection using an iterator -- pointers to the beginning and
// the ending element plus one.
//
hr = pCollection->Initialize( m_Terminals.GetSize(),
m_Terminals.GetData(),
m_Terminals.GetData() + m_Terminals.GetSize() );
if (FAILED(hr))
{
LOG((MSP_ERROR, "CMSPStream::get_Terminals - "
"Initialize on collection failed - exit 0x%08x", hr));
pDispatch->Release();
return hr;
}
//
// put the IDispatch interface pointer into the variant
//
LOG((MSP_TRACE, "CMSPStream::get_Terminals - "
"placing IDispatch value %08x in variant", pDispatch));
VariantInit(pVariant);
pVariant->vt = VT_DISPATCH;
pVariant->pdispVal = pDispatch;
LOG((MSP_TRACE, "CMSPStream::get_Terminals - exit S_OK"));
return S_OK;
}
STDMETHODIMP CMSPStream::StartStream()
{
LOG((MSP_TRACE, "CMSPStream - RUNNING GRAPH"));
HRESULT hr = m_pIMediaControl->Run();
if(FAILED(hr))
{
LOG((MSP_ERROR, "graph doesn't run, %x", hr));
}
return hr;
}
STDMETHODIMP CMSPStream::PauseStream()
{
LOG((MSP_TRACE, "CMSPStream - PAUSING GRAPH"));
HRESULT hr = m_pIMediaControl->Pause();
if(FAILED(hr))
{
LOG((MSP_ERROR, "graph doesn't pause, %x", hr));
}
return hr;
}
STDMETHODIMP CMSPStream::StopStream()
{
LOG((MSP_TRACE, "CMSPStream - STOPPING GRAPH"));
HRESULT hr = m_pIMediaControl->Stop();
if(FAILED(hr))
{
LOG((MSP_ERROR, "graph doesn't stop, %x", hr));
}
return hr;
}
// methods called by the MSPCall object.
HRESULT CMSPStream::Init(
IN HANDLE hAddress,
IN CMSPCallBase * pMSPCall,
IN IMediaEvent * pGraph,
IN DWORD dwMediaType,
IN TERMINAL_DIRECTION Direction
)
{
LOG((MSP_TRACE, "CMSPStream::Init - enter"));
CLock lock(m_lock);
// This method is called only once when the object is created. No other
// method will be called until this function succeeds. No need to lock.
_ASSERTE(m_hAddress == NULL);
// initialize the terminal array so that the array is not NULL. Used for
// generating an empty enumerator if no terminal is selected.
if (!m_Terminals.Grow())
{
LOG((MSP_ERROR, "CMSPStream::Init - exit E_OUTOFMEMORY"));
return E_OUTOFMEMORY;
}
HRESULT hr;
hr = CoCreateFreeThreadedMarshaler(GetControllingUnknown(), &m_pFTM);
if (FAILED(hr))
{
LOG((MSP_ERROR, "create marshaler failed, %x", hr));
return hr;
}
// Get the media control interface on the graph.
IMediaControl *pMC;
hr = pGraph->QueryInterface(IID_IMediaControl, (void **) &pMC);
if(FAILED(hr))
{
LOG((MSP_ERROR, "get IMediaControl interface, %x", hr));
return hr;
}
// Get the graph builder interface on the graph.
IGraphBuilder *pGB;
hr = pGraph->QueryInterface(IID_IGraphBuilder, (void **) &pGB);
if(FAILED(hr))
{
LOG((MSP_ERROR, "get IGraphBuilder interface, %x", hr));
pMC->Release();
return hr;
}
m_hAddress = hAddress;
m_pMSPCall = pMSPCall;
m_pMSPCall->MSPCallAddRef();
m_pIMediaControl = pMC;
// no addref because QI addrefs for us above
m_pIGraphBuilder = pGB;
// no addref because QI addrefs for us above
m_dwMediaType = dwMediaType;
m_Direction = Direction;
LOG((MSP_TRACE, "CMSPStream::Init - exit S_OK"));
return S_OK;
}
HRESULT CMSPStream::ShutDown()
{
LOG((MSP_TRACE, "CMSPStream::Shutdown - enter"));
CLock lock(m_lock);
//
// We are shut down, so the call is now NULL.
//
m_pMSPCall->MSPCallRelease();
m_pMSPCall = NULL;
//
// Unselect all the terminals. Rather than just removing all the
// terminals, we call UnselectTerminal on each one to give the derived
// class a chance to do whatever else it needs to do when a terminal
// is unselected.
//
// We walk the list in reverse order because the list shrinks with
// each iteration (see msputils.h).
//
for ( int i = m_Terminals.GetSize() - 1; i >= 0; i-- )
{
UnselectTerminal(m_Terminals[i]);
}
//
// At this point the derive class should have removed and released
// all of the terminals from the list. If that's not the case, the
// derived class is buggy.
//
_ASSERTE( m_Terminals.GetSize() == 0 );
//
// no longer need the sink
//
ReleaseSink();
LOG((MSP_TRACE, "CMSPStream::Shutdown - exit S_OK"));
return S_OK;
}
void CMSPStream::FinalRelease()
{
LOG((MSP_TRACE, "CMSPStream::FinalRelease - enter"));
// release the two interface pointers to the graph.
if (m_pIMediaControl)
{
m_pIMediaControl->Release();
}
if (m_pIGraphBuilder)
{
m_pIGraphBuilder->Release();
}
if (m_pFTM)
{
m_pFTM->Release();
}
LOG((MSP_TRACE, "CMSPStream::FinalRelease - exit"));
}
HRESULT CMSPStream::HandleTSPData(
IN BYTE * pData,
IN DWORD dwSize
)
{
LOG((MSP_TRACE, "CMSPStream::HandleTSPData - enter"));
LOG((MSP_TRACE, "CMSPStream::HandleTSPData - exit S_OK"));
return S_OK;
}
HRESULT CMSPStream::ProcessGraphEvent(
IN long lEventCode,
IN LONG_PTR lParam1,
IN LONG_PTR lParam2
)
{
LOG((MSP_TRACE, "CMSPStream::ProcessGraphEvent - enter"));
LOG((MSP_TRACE, "CMSPStream::ProcessGraphEvent - exit S_OK"));
return S_OK;
}
/*++
RegisterPluggableTerminalEventSink
Parameters:
The terminla interface
Description;
Is called by SelectTerminal if is a dynamic terminal
--*/
HRESULT CMSPStream::RegisterPluggableTerminalEventSink(
IN ITTerminal* pTerminal
)
{
LOG((MSP_TRACE, "CMSPStream::RegisterPluggableTerminalEventSink - enter"));
//
// Validates arguments
//
if( IsBadReadPtr( pTerminal, sizeof( ITTerminal) ))
{
LOG((MSP_ERROR, "CMSPStream::RegisterPluggableTerminalEventSink "
"pTerminal invalid, returns E_POINTER"));
return E_POINTER;
}
//
// Get the type of the terminal
//
TERMINAL_TYPE nTerminalType;
HRESULT hr = E_FAIL;
hr = pTerminal->get_TerminalType( &nTerminalType );
if( FAILED(hr) )
{
LOG((MSP_ERROR, "CMSPStream::RegisterPluggableTerminalEventSink "
"get_TerminalType failed, exit E_UNEXPECTED"));
return E_UNEXPECTED;
}
//
// The terminal should by a dynamic terminal
//
if( TT_DYNAMIC != nTerminalType)
{
LOG((MSP_ERROR, "CMSPStream::RegisterPluggableTerminalEventSink "
"terminal is not dynamic, exit E_INVALIDARG"));
return E_INVALIDARG;
}
CLock lock(m_lock);
//
// Create the sink if we don't have one already
//
if(NULL == m_pPTEventSink)
{
//Create a PTEventSink object
CComObject<CPTEventSink>* pPTEventSink;
hr = CComObject<CPTEventSink>::CreateInstance(&pPTEventSink);
if( FAILED(hr) )
{
LOG((MSP_ERROR, "CMSPStream::RegisterPluggableTerminalEventSink "
"CreateInstance failed, returns E_OUTOFMEMORY"));
return E_OUTOFMEMORY;
}
// tell sink that we are ready to be processing its events
hr = pPTEventSink->SetSinkStream(this);
if (FAILED(hr))
{
LOG((MSP_ERROR, "CMSPStream::RegisterPluggableTerminalEventSink "
"event sink refused to accept sink stream. hr = %lx", hr));
delete pPTEventSink;
return hr;
}
// Get ITPluggableTerminalEventSink interface from the sink
hr = pPTEventSink->QueryInterface(IID_ITPluggableTerminalEventSink, (void**)&m_pPTEventSink);
if( FAILED(hr) )
{
LOG((MSP_ERROR, "CMSPStream::RegisterPluggableTerminalEventSink "
"QI for ITPluggableTerminalEventSink failed, returns E_UNEXPECTED"));
//
// ok, the sink is no good. get rid of it.
//
pPTEventSink->SetSinkStream(NULL);
delete pPTEventSink;
pPTEventSink = NULL;
//
// sink does not expose IID_ITPluggableTerminalEventSink interface?
// something is seriously wrong
//
return E_UNEXPECTED;
}
}
// Get the ITDTEventHandler interface
ITPluggableTerminalEventSinkRegistration* pEventRegistration = NULL;
hr = pTerminal->QueryInterface( IID_ITPluggableTerminalEventSinkRegistration,
(void**)&pEventRegistration
);
if( FAILED(hr) )
{
// The dynamic terminal doesn't implement ITPluggableTerminalEventSinkRegistration
// This is bad! We cannot use the new event stuff
LOG((MSP_ERROR, "CMSPStream::RegisterPluggableTerminalEventSink "
"QI for ITPluggableTerminalEventSinkregistration failed, returns S_FALSE"));
//
// no need to keep the sink
//
ReleaseSink();
return S_FALSE;
}
// pass the sink to the terminal
hr = pEventRegistration->RegisterSink(
m_pPTEventSink
);
// Clean up, anyway
pEventRegistration->Release();
if( FAILED(hr) )
{
LOG((MSP_ERROR, "CMSPStream::RegisterPluggableTerminalEventSink "
"RegisterSink failed, returns E_FAIL"));
//
// no need to keep the sink
//
ReleaseSink();
return E_FAIL;
}
LOG((MSP_TRACE, "CMSPStream::RegisterPluggableTerminalEventSink - exit S_OK"));
return S_OK;
}
/*++
UnregisterPluggableTerminalEventSink
Parameters:
The terminal interface
Description;
Is called by UnselectTerminal if is a dynamic terminal
--*/
HRESULT CMSPStream::UnregisterPluggableTerminalEventSink(
IN ITTerminal* pTerminal
)
{
LOG((MSP_TRACE, "CMSPStream::UnregisterPluggableTerminalEventSink - enter"));
//
// Validates arguments
//
if( IsBadReadPtr( pTerminal, sizeof( ITTerminal) ))
{
LOG((MSP_ERROR, "CMSPStream::UnregisterPluggableTerminalEventSink "
"pTerminal invalid, returns E_POINTER"));
return E_POINTER;
}
//
// Get the type of the terminal
//
TERMINAL_TYPE nTerminalType;
HRESULT hr = E_FAIL;
hr = pTerminal->get_TerminalType( &nTerminalType );
if( FAILED(hr) )
{
LOG((MSP_ERROR, "CMSPStream::UnregisterPluggableTerminalEventSink "
"get_TerminalType failed, exit E_UNEXPECTED"));
return E_UNEXPECTED;
}
//
// The terminal should be a dynamic terminal
//
if( TT_DYNAMIC != nTerminalType)
{
LOG((MSP_ERROR, "CMSPStream::UnregisterPluggableTerminalEventSink "
"terminal is not dynamic, exit E_INVALIDARG"));
return E_INVALIDARG;
}
CLock lock(m_lock);
//
// Have we an EventSink object
//
if(NULL == m_pPTEventSink)
{
LOG((MSP_TRACE, "CMSPStream::UnregisterPluggableTerminalEventSink - "
"No EventSink - exit S_OK"));
return S_OK;
}
//
// Get the ITPluggableTemrinalEventSinkRegistration interface
//
ITPluggableTerminalEventSinkRegistration* pEventRegistration = NULL;
hr = pTerminal->QueryInterface( IID_ITPluggableTerminalEventSinkRegistration,
(void**)&pEventRegistration
);
if( FAILED(hr) )
{
//
// The pluggable terminal doesn't implement ITPluggableTerminalEventSinkRegistration
// This is bad!
LOG((MSP_ERROR, "CMSPStream::UnregisterPluggableTerminalEventSink "
"QI for ITPluggableTerminalEventSinkRegistration failed, returns E_NOTIMPL"));
return E_NOTIMPL;
}
hr = pEventRegistration->UnregisterSink( );
//
// Clean up, anyway
//
pEventRegistration->Release();
if( FAILED(hr) )
{
LOG((MSP_ERROR, "CMSPStream::UnregisterPluggableTerminalEventSink "
"UnregisterSink failed, returns E_FAIL"));
return E_FAIL;
}
//
// no longer need this sink
//
ReleaseSink();
LOG((MSP_TRACE, "CMSPStream::UnregisterPluggableTerminalEventSink - exit S_OK"));
return S_OK;
}
//////////////////////////////////////////////////////////////////////////////
//
// CMSPStream::HandleSinkEvent
//
//
// CPTEventSink calls this method when it has an event for us to process
// HandleSinkEvent delegates event processing to the call, if we have one
//
//////////////////////////////////////////////////////////////////////////////
HRESULT CMSPStream::HandleSinkEvent(MSPEVENTITEM *pEventItem)
{
LOG((MSP_TRACE, "CMSPStream::HandleSinkEvent - enter"));
HRESULT hr = TAPI_E_CALLUNAVAIL;
CLock lock(m_lock);
if (NULL != m_pMSPCall)
{
//
// we have a call. ask it to process the event
//
hr = m_pMSPCall->HandleStreamEvent(pEventItem);
}
else
{
LOG((MSP_WARN,
"CMSPStream::HandleSinkEvent - there is no call to pass event to"));
}
LOG((MSP_(hr), "CMSPStream::HandleSinkEvent - exit hr = %lx", hr));
return hr;
}
//////////////////////////////////////////////////////////////////////////////
//
// CMSPStream::ReleaseSink
//
//
// this is a helper function that lets go of sink when it no longer needed
//
//////////////////////////////////////////////////////////////////////////////
HRESULT CMSPStream::ReleaseSink()
{
LOG((MSP_TRACE, "CMSPStream::ReleaseSink - enter"));
HRESULT hr = S_OK;
CLock lock(m_lock);
//
// if sink is present, let it know that we will no longer be available to
// process its events and release it
//
if( m_pPTEventSink)
{
CPTEventSink *pSinkObject = static_cast<CPTEventSink *>(m_pPTEventSink);
HRESULT hr = pSinkObject->SetSinkStream(NULL);
if (FAILED(hr))
{
LOG((MSP_ERROR,
"CMSPStream::ReleaseSink - pSinkObject->SetSinkStream failed. hr - %lx",
hr));
}
m_pPTEventSink->Release();
m_pPTEventSink = NULL;
}
LOG((MSP_(hr), "CMSPStream::ReleaseSink - exit. hr - %lx", hr));
return hr;
}
// eof