/*++ Copyright (c) 1998-1999 Microsoft Corporation Module Name: mspterm.cpp Abstract: Implementations for the CBaseTerminal, CSingleFilterTerminal, and various work item / worker thread classes. --*/ #include "precomp.h" #pragma hdrstop //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ CBaseTerminal::CBaseTerminal() : m_TerminalDirection(TD_CAPTURE) , m_TerminalType(TT_STATIC) , m_TerminalState(TS_NOTINUSE) , m_TerminalClassID(CLSID_NULL) , m_pFTM(NULL) { LOG((MSP_TRACE, "CBaseTerminal::CBaseTerminal() called")); HRESULT hr = CoCreateFreeThreadedMarshaler( GetControllingUnknown(), &m_pFTM); if ( FAILED(hr) ) { LOG((MSP_TRACE, "CBaseTerminal::CBaseTerminal() - create ftm failed")); } } //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ CBaseTerminal::~CBaseTerminal() { if (NULL != m_pFTM) { m_pFTM->Release(); } LOG((MSP_TRACE, "CBaseTerminal::~CBaseTerminal() finished")); } //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // // Dynamic terminals that support only one direction must override this to check // if the direction is valid (although the terminal manager is supposed to // ensure that the wrong direction is never passed). Dynamic terminal might want // to override this for other reasons too (create filters now, etc.). // // Static terminals normally just call this in their CreateTerminal(). // HRESULT CBaseTerminal::Initialize( IN IID iidTerminalClass, IN DWORD dwMediaType, IN TERMINAL_DIRECTION Direction, IN MSP_HANDLE htAddress ) { CLock lock(m_CritSec); LOG((MSP_TRACE, "CBaseTerminal::Initialize - enter")); // // Check if the media type is supported by this terminal. // if ( ! MediaTypeSupported( (long) dwMediaType) ) { LOG((MSP_ERROR, "CBaseTerminal::Initialize - " "media type not supported - returning E_INVALIDARG")); return E_INVALIDARG; } // // Save this configurarion. // m_dwMediaType = dwMediaType; m_TerminalDirection = Direction; m_TerminalClassID = iidTerminalClass; m_htAddress = htAddress; LOG((MSP_TRACE, "CBaseTerminal::Initialize - exit S_OK")); return S_OK; } //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ STDMETHODIMP CBaseTerminal::get_AddressHandle ( OUT MSP_HANDLE * phtAddress ) { CLock lock(m_CritSec); LOG((MSP_TRACE, "CBaseTerminal::get_AddressHandle - enter")); if ( MSPB_IsBadWritePtr( phtAddress, sizeof(MSP_HANDLE) ) ) { LOG((MSP_ERROR, "CBaseTerminal::get_AddressHandle - returning E_POINTER")); return E_POINTER; } *phtAddress = m_htAddress; LOG((MSP_TRACE, "CBaseTerminal::get_AddressHandle - exit S_OK")); return S_OK; } //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ STDMETHODIMP CBaseTerminal::get_Name(BSTR * pbsName) { CLock lock(m_CritSec); LOG((MSP_TRACE, "CBaseTerminal::get_Name - enter")); if ( MSPB_IsBadWritePtr( pbsName, sizeof(BSTR) ) ) { LOG((MSP_ERROR, "CBaseTerminal::get_Name - " "bad BSTR passed in - returning E_POINTER")); return E_POINTER; } *pbsName = SysAllocString(m_szName); if ( *pbsName == NULL ) { LOG((MSP_ERROR, "CBaseTerminal::get_Name - " "can't sysallocstring - returning E_OUTOFMEMORY")); return E_OUTOFMEMORY; } LOG((MSP_TRACE, "CBaseTerminal::get_Name - exit S_OK")); return S_OK; } //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ STDMETHODIMP CBaseTerminal::get_State(TERMINAL_STATE * pVal) { CLock lock(m_CritSec); LOG((MSP_TRACE, "CBaseTerminal::get_State - enter")); if ( MSPB_IsBadWritePtr( pVal, sizeof(TERMINAL_STATE) ) ) { LOG((MSP_ERROR, "CBaseTerminal::get_State - returning E_POINTER")); return E_POINTER; } *pVal = m_TerminalState; LOG((MSP_TRACE, "CBaseTerminal::get_State - exit S_OK")); return S_OK; } //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ STDMETHODIMP CBaseTerminal::get_TerminalType(TERMINAL_TYPE * pVal) { CLock lock(m_CritSec); LOG((MSP_TRACE, "CBaseTerminal::get_TerminalType - enter")); if ( MSPB_IsBadWritePtr( pVal, sizeof(TERMINAL_TYPE) ) ) { LOG((MSP_ERROR, "CBaseTerminal::get_TerminalType - returning E_POINTER")); return E_POINTER; } *pVal = m_TerminalType; LOG((MSP_TRACE, "CBaseTerminal::get_TerminalType - exit S_OK")); return S_OK; } //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ STDMETHODIMP CBaseTerminal::get_TerminalClass(BSTR * pbsClassID) { CLock lock(m_CritSec); LOG((MSP_TRACE, "CBaseTerminal::get_TerminalClass - enter")); if ( MSPB_IsBadWritePtr( pbsClassID, sizeof(BSTR) ) ) { LOG((MSP_ERROR, "CBaseTerminal::get_TerminalClass - returning E_POINTER")); return E_POINTER; } // // Convert the CLSID to an OLE string. // LPOLESTR lposClass = NULL; HRESULT hr = StringFromCLSID(m_TerminalClassID, &lposClass); if (FAILED(hr)) { LOG((MSP_ERROR, "CBaseTerminal::get_TerminalClass (StringFromCLSID) - returning %8x", hr)); return hr; } // // Put the string in a BSTR. // *pbsClassID = ::SysAllocString(lposClass); // // Free the OLE string. // ::CoTaskMemFree(lposClass); if (*pbsClassID == NULL) { LOG((MSP_ERROR, "CBaseTerminal::get_TerminalClass - returning E_OUTOFMEMORY")); return E_OUTOFMEMORY; } LOG((MSP_TRACE, "CBaseTerminal::get_TerminalClass - exit S_OK")); return S_OK; } //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ STDMETHODIMP CBaseTerminal::get_Direction( OUT TERMINAL_DIRECTION *pDirection ) { CLock lock(m_CritSec); LOG((MSP_TRACE, "CBaseTerminal::get_Direction - enter")); if ( MSPB_IsBadWritePtr( pDirection, sizeof(TERMINAL_DIRECTION) ) ) { LOG((MSP_ERROR, "CBaseTerminal::get_Direction - returning E_POINTER")); return E_POINTER; } *pDirection = m_TerminalDirection; LOG((MSP_TRACE, "CBaseTerminal::get_Direction - exit S_OK")); return S_OK; } //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // enters each of the internal filters into the filter graph // connects the internal filters together (if applicable) // and returns all the filters to be used as connection points STDMETHODIMP CBaseTerminal::ConnectTerminal( IN IGraphBuilder * pGraph, IN DWORD dwTerminalDirection, IN OUT DWORD * pdwNumPins, OUT IPin ** ppPins ) { LOG((MSP_TRACE, "CBaseTerminal::ConnectTerminal - enter")); // // Check parameters. // if ( IsBadReadPtr(pGraph, sizeof(IGraphBuilder) ) ) { LOG((MSP_ERROR, "CBaseTerminal::ConnectTerminal - " "bad graph pointer; exit E_POINTER")); return E_POINTER; } if ( MSPB_IsBadWritePtr(pdwNumPins, sizeof(DWORD) ) ) { LOG((MSP_ERROR, "CBaseTerminal::ConnectTerminal - " "bad numpins pointer; exit E_POINTER")); return E_POINTER; } // // Find out how many pins we expose. For most terminals this is // straightforward but we pass in the graph pointer in case they // need to do something funky to figure this out. // DWORD dwActualNumPins; HRESULT hr; hr = GetNumExposedPins(pGraph, &dwActualNumPins); if ( FAILED(hr) ) { LOG((MSP_ERROR, "CBaseTerminal::ConnectTerminal - " "GetNumExposedPins failed - exit 0x%08x", hr)); return hr; } // // If ppPins is NULL, just return the number of pins and don't try to // connect the terminal. // if ( ppPins == NULL ) { LOG((MSP_TRACE, "CBaseTerminal::ConnectTerminal - " "returned number of exposed pins - exit S_OK")); *pdwNumPins = dwActualNumPins; return S_OK; } // // Otherwise, we have a pin return buffer. Check that the purported buffer // size is big enough and that the buffer is actually writable to the size // we need. // if ( *pdwNumPins < dwActualNumPins ) { LOG((MSP_ERROR, "CBaseTerminal::ConnectTerminal - " "not enough space to place pins; exit TAPI_E_NOTENOUGHMEMORY")); *pdwNumPins = dwActualNumPins; return TAPI_E_NOTENOUGHMEMORY; } if ( MSPB_IsBadWritePtr(ppPins, dwActualNumPins * sizeof(IPin *) ) ) { LOG((MSP_ERROR, "CBaseTerminal::ConnectTerminal - " "bad pins array pointer; exit E_POINTER")); return E_POINTER; } // // Check if we're already connected, and if so, change our state to // connected. Note that this makes sense for both core static terminals // and dynamic terminals. Also note that we need to protect this with // a critical section, but after this we can let go of the lock because // anyone who subsequently enters the critical section will bail at this // point. // { CLock lock(m_CritSec); // // check if already connected // if (TS_INUSE == m_TerminalState) { LOG((MSP_ERROR, "CBaseTerminal::ConnectTerminal - " "terminal already in use; exit TAPI_E_TERMINALINUSE")); return TAPI_E_TERMINALINUSE; } // // Save important state. // m_pGraph = pGraph; m_TerminalState = TS_INUSE; } // add filters to the filter graph hr = AddFiltersToGraph(); if ( FAILED(hr) ) { LOG((MSP_ERROR, "CBaseTerminal::ConnectTerminal - " "can't add filters to graph")); goto disconnect_terminal; } // Give the terminal a chance to do any preconnection hr = ConnectFilters(); if ( FAILED(hr) ) { LOG((MSP_ERROR, "CBaseTerminal::ConnectTerminal - " "can't do internal filter connection")); goto disconnect_terminal; } // // Get the pins that this filter exposes. No need to pass in // the filter graph because we already saved the graph pointer. // *pdwNumPins = dwActualNumPins; hr = GetExposedPins(ppPins); if ( FAILED(hr) ) { LOG((MSP_ERROR, "CBaseTerminal::ConnectTerminal - " "can't get exposed pins")); goto disconnect_terminal; } LOG((MSP_TRACE, "CBaseTerminal::ConnectTerminal success")); return S_OK; disconnect_terminal: // // best effort attempt to disconnect - ignore error code // DisconnectTerminal(pGraph, 0); // // Release our reference to the graph and set ourselves to notinuse state. // DisconnectTerminal does this on success, but we need to make sure this // cleanup happens even if DisconnectTerminal failed. // m_pGraph = NULL; // this releases the CComPtr m_TerminalState = TS_NOTINUSE; LOG((MSP_TRACE, "CBaseTerminal::ConnectTerminal - exit 0x%08x", hr)); return hr; } //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ STDMETHODIMP CBaseTerminal::CompleteConnectTerminal(void) { LOG((MSP_TRACE, "CBaseTerminal::CompleteConnectTerminal - enter")); LOG((MSP_TRACE, "CBaseTerminal::CompleteConnectTerminal - exit S_OK")); return S_OK; } //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // disconnects the internal filters from each other (if applicable) // and removes them from the filter graph (thus breaking connections to // the stream). // Filter graph parameter is used for validation, to make sure the terminal // is disconnected from the same graph that it was originally connected to. STDMETHODIMP CBaseTerminal::DisconnectTerminal( IN IGraphBuilder * pGraph, IN DWORD dwReserved ) { CLock lock(m_CritSec); LOG((MSP_TRACE, "CBaseTerminal::DisconnectTerminal called")); // // If not in use, then there is nothing to be done. // if ( TS_INUSE != m_TerminalState ) { _ASSERTE(m_pGraph == NULL); LOG((MSP_TRACE, "CBaseTerminal::DisconnectTerminal success; not in use")); return S_OK; } // // Check that we are being disconnected from the correct graph. // if ( m_pGraph != pGraph ) { LOG((MSP_TRACE, "CBaseTerminal::DisconnectTerminal - " "wrong graph; returning E_INVALIDARG")); return E_INVALIDARG; } // // Extra sanity check. // if ( m_pGraph == NULL ) { LOG((MSP_TRACE, "CBaseTerminal::DisconnectTerminal - " "no graph; returning E_UNEXPECTED")); return E_UNEXPECTED; } HRESULT hr; // // Remove filters from the graph // hr = RemoveFiltersFromGraph(); if ( FAILED(hr) ) { LOG((MSP_ERROR, "CBaseTerminal::DisconnectTerminal - " "remove filters from graph failed; returning 0x%08x", hr)); return hr; } // // Release our reference to the graph and set ourselves to notinuse state. // m_pGraph = NULL; // this releases the CComPtr m_TerminalState = TS_NOTINUSE; LOG((MSP_TRACE, "CBaseTerminal::DisconnectTerminal success")); return S_OK; } //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ STDMETHODIMP CBaseTerminal::get_MediaType(long * plMediaType) { CLock lock(m_CritSec); LOG((MSP_TRACE, "CBaseTerminal::get_MediaType - enter")); if ( MSPB_IsBadWritePtr(plMediaType, sizeof(long) ) ) { LOG((MSP_ERROR, "CBaseTerminal::get_MediaType - returning E_POINTER")); return E_POINTER; } *plMediaType = (long) m_dwMediaType; LOG((MSP_TRACE, "CBaseTerminal::get_MediaType - exit S_OK")); return S_OK; } //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ BOOL CBaseTerminal::MediaTypeSupported(long lMediaType) { return IsValidSingleMediaType( (DWORD) lMediaType, GetSupportedMediaTypes() ); } ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// // // // CSingleFilterTerminal // // // // This is a base class for a terminal with a single filter and pin. The // // terminal could be any direction or media type, and it could be static // // or dynamic. // // // ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// HRESULT CSingleFilterTerminal::GetNumExposedPins( IN IGraphBuilder * pGraph, OUT DWORD * pdwNumPins) { LOG((MSP_TRACE, "CSingleFilterTerminal::GetNumExposedPins - enter")); // // We ignote pGraph because we don't need to do anything special to find // out how many pins we have. // *pdwNumPins = 1; LOG((MSP_TRACE, "CSingleFilterTerminal::GetNumExposedPins - exit S_OK")); return S_OK; } //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ HRESULT CSingleFilterTerminal::GetExposedPins( OUT IPin ** ppPins ) { LOG((MSP_TRACE, "CSingleFilterTerminal::GetExposedPins - enter")); _ASSERTE( ! MSPB_IsBadWritePtr(ppPins, 1 * sizeof(IPin *) ) ); // // Return our single pin. // *ppPins = m_pIPin; (*ppPins)->AddRef(); LOG((MSP_TRACE, "CSingleFilterTerminal::GetExposedPins - exit S_OK")); return S_OK; } //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // stops the rightmost render filter in the terminal // (needed for dynamic filter graphs) STDMETHODIMP CSingleFilterTerminal::RunRenderFilter(void) { // check that we're really a render filter // tell our single filter to run return E_NOTIMPL; } //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // stops the rightmost render filter in the terminal // (needed for dynamic filter graphs) STDMETHODIMP CSingleFilterTerminal::StopRenderFilter(void) { // check that we're really a render filter // tell our single filter to stop return E_NOTIMPL; } //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ HRESULT CSingleFilterTerminal::RemoveFiltersFromGraph(void) { LOG((MSP_TRACE, "CSingleFilterTerminal::RemoveFiltersFromGraph - enter")); if (m_pGraph == NULL) { LOG((MSP_ERROR, "CSingleFilterTerminal::RemoveFiltersFromGraph - " "no graph; returning E_UNEXPECTED")); return E_UNEXPECTED; } if (m_pIFilter == NULL) { LOG((MSP_ERROR, "CSingleFilterTerminal::RemoveFiltersFromGraph - " "no filter; returning E_UNEXPECTED")); return E_UNEXPECTED; } // // Remove the filter from the graph. This also disconnects any connections // the filter may have. // HRESULT hr = m_pGraph->RemoveFilter(m_pIFilter); LOG((MSP_TRACE, "CSingleFilterTerminal::RemoveFiltersFromGraph - exit 0x%08x", hr)); return hr; } HRESULT CSingleFilterStaticTerminal::CompareMoniker( IMoniker *pMoniker ) { IMoniker *pReducedMoniker; IMoniker *pReducedNewMoniker; IBindCtx *pbc; HRESULT hr; hr = CreateBindCtx( 0, &pbc ); if (FAILED(hr)) { LOG((MSP_ERROR, "CSingleFilterStaticTerminal::CompareMoniker - " "unable to create bind context")); return hr; } hr = m_pMoniker->Reduce(pbc ,MKRREDUCE_ALL, NULL, &pReducedMoniker); if (FAILED(hr) || !pReducedMoniker) { LOG((MSP_ERROR, "CSingleFilterStaticTerminal::CompareMoniker - " "unable to reduce moniker")); pbc->Release(); // release the bind context return hr; } hr = pMoniker->Reduce(pbc ,MKRREDUCE_ALL, NULL, &pReducedNewMoniker); if (FAILED(hr) || !pReducedNewMoniker) { LOG((MSP_ERROR, "CSingleFilterStaticTerminal::CompareMoniker - " "unable to reduce moniker")); pbc->Release(); // release the bind context pReducedMoniker->Release(); // release the reduced moniker return hr; } pbc->Release(); // release the bind context if (pReducedMoniker->IsEqual(pReducedNewMoniker) == S_OK) { LOG((MSP_TRACE, "CSingleFilterStaticTerminal::CompareMoniker - " "exit - return S_OK")); pReducedMoniker->Release(); // release the reduced monikers pReducedNewMoniker->Release(); return S_OK; } pReducedMoniker->Release(); // release the reduced monikers pReducedNewMoniker->Release(); LOG((MSP_TRACE, "CSingleFilterStaticTerminal::CompareMoniker - " "exit - return S_FALSE")); return S_FALSE; } // eof