/*++ 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 CCopy; typedef CSafeComEnum CEnumerator; HRESULT hr; CMSPComObject *pEnum = NULL; hr = CMSPComObject::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 * pCollection; hr = CComObject::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* pPTEventSink; hr = CComObject::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(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