/* Copyright (c) 1998-1999 Microsoft Corporation */ #include "stdafx.h" #include "atlconv.h" #include "termmgr.h" #include "meterf.h" #include "newmes.h" /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// // These will be obsolete when we derive from CSingleFilterTerminal HRESULT CMediaTerminal::GetNumExposedPins( IN IGraphBuilder * pGraph, OUT DWORD * pdwNumPins) { LOG((MSP_TRACE, "CMediaTerminal::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, "CMediaTerminal::GetNumExposedPins - exit S_OK")); return S_OK; } HRESULT CMediaTerminal::GetExposedPins( OUT IPin ** ppPins ) { LOG((MSP_TRACE, "CMediaTerminal::GetExposedPins - enter")); TM_ASSERT( ! TM_IsBadWritePtr(ppPins, 1 * sizeof(IPin *) ) ); // // Return our single pin. // *ppPins = m_pOwnPin; (*ppPins)->AddRef(); LOG((MSP_TRACE, "CMediaTerminal::GetExposedPins - exit S_OK")); return S_OK; } /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// // for CLSID_MediaStreamFilter // "amguids.h" has the guid value // but requires #define INITGUID before including compobj.h EXTERN_C const GUID CLSID_MediaStreamFilter = { 0x49c47ce0, 0x9ba4, 0x11d0, {0x82, 0x12, 0x00, 0xc0, 0x4f, 0xc3, 0x2c, 0x45} }; // this is used for indexing into the friendly name array // it is needed because neither the tapi media type (string guid) nor // the IMediaStream media consts (not an enum) can be used enum MEDIA_STREAM_TERMINAL_MEDIA { MEDIA_STREAM_TERMINAL_AUDIO=0, MEDIA_STREAM_TERMINAL_VIDEO }; // the MEDIA_STREAM_TERMINAL_MEDIA and TERMINAL_DIRECTION values are used as // indices into this array to determine the friendly name // these should ideally be const WCHAR *, but the base class member m_szName is // a pointer to BSTR (should be const WCHAR * as per current usage) DWORD gs_MSTFriendlyName[2][2] = { { IDS_MSTR_AUDIO_WRITE, // capture IDS_MSTR_AUDIO_READ, // render }, { IDS_MSTR_VIDEO_WRITE, // capture IDS_MSTR_VIDEO_READ, // render } }; // CMediaTerminal STDMETHODIMP CMediaTerminal::InitializeDynamic ( IN IID iidTerminalClass, IN DWORD dwMediaType, IN TERMINAL_DIRECTION Direction, IN MSP_HANDLE htAddress ) { LOG((MSP_TRACE, "CMediaTerminal::Initialize - enter")); // // We are OK with either direction. Just do the base class method... // HRESULT hr; hr = CBaseTerminal::Initialize(iidTerminalClass, dwMediaType, Direction, htAddress); if ( FAILED(hr) ) { LOG((MSP_ERROR, "CVideoRenderTerminal::Initialize - " "base class method failed - returning 0x%08x", hr)); return hr; } // // Now do our own initialization: // // sets certain member variables // ex. m_TerminalType, m_szName // initialize the aggregated filter // MSPID PurposeId; STREAM_TYPE StreamType; const GUID *pAmovieMajorType; // uses pTapiMediaType, TerminalDirection to determine the // purpose id and stream type. // sets PurposeId, StreamType, pAmovieMajorType among others hr = SetNameInfo( (long) dwMediaType, Direction, PurposeId, StreamType, pAmovieMajorType ); BAIL_ON_FAILURE(hr); ASSERT(NULL != pAmovieMajorType); // initialize the aggregated filter ASSERT(NULL != m_pAggTerminalFilter); hr = m_pAggTerminalFilter->Init(PurposeId, StreamType, *pAmovieMajorType); BAIL_ON_FAILURE(hr); LOG((MSP_TRACE, "CMediaTerminal::Initialize - exit S_OK")); return S_OK; } // free the allocated member variables HRESULT CMediaTerminal::FinalConstruct( ) { LOG((MSP_TRACE, "CMediaTerminal::FinalConstruct called")); HRESULT hr; m_pAggInstance = new FILTER_COM_OBJECT(GetControllingUnknown()); BAIL_IF_NULL(m_pAggInstance, E_OUTOFMEMORY); hr = m_pAggInstance->FinalConstruct(); if (HRESULT_FAILURE(hr)) { // delete the aggregating instance delete m_pAggInstance; return hr; } // we get the nondelegating IUnknown i/f of the aggregating shell // around the contained object. keep this refcnt around during our // lifetime hr = m_pAggInstance->QueryInterface( IID_IUnknown, (void **)&m_pIUnkTerminalFilter ); if ( FAILED(hr) ) { // must call final release m_pAggInstance->FinalRelease(); // delete the aggregating instance delete m_pAggInstance; return hr; } // these query interface calls increase our own refcnt // release the refcnt as soon as the interface is obtained // these shouldn't be CComPtrs as they are weak references hr = m_pAggInstance->QueryInterface( IID_IPin, (void **)&m_pOwnPin ); if ( FAILED(hr) ) { goto error; } if (NULL != m_pOwnPin) { m_pOwnPin->Release(); } hr = m_pAggInstance->QueryInterface( IID_IAMMediaStream, (void **)&m_pIAMMediaStream ); if ( FAILED(hr) ) { goto error; } if (NULL != m_pIAMMediaStream) { m_pIAMMediaStream->Release(); } // point m_pAggTerminalFilter to the contained member of the // aggregating instance m_pAggTerminalFilter = &m_pAggInstance->m_contained; LOG((MSP_TRACE, "CMediaTerminal::FinalConstruct succeeded")); return S_OK; error: // we come here in case of errors after calling FinalConstruct ASSERT( FAILED(hr) ); // final release the aggregating shell ASSERT(NULL != m_pAggInstance); m_pAggInstance->FinalRelease(); // null any CComPtrs // this should destroy the aggregated instance and the contained // media terminal filter m_pIUnkTerminalFilter = NULL; LOG((MSP_TRACE, "CMediaTerminal::FinalConstruct failed")); return hr; } void CMediaTerminal::FinalRelease( ) { LOG((MSP_TRACE, "CMediaTerminal::FinalRelease called")); // final release the aggregating shell ASSERT(NULL != m_pAggInstance); m_pAggInstance->FinalRelease(); // null any CComPtrs // this should destroy the aggregating instance and the contained // media terminal filter m_pIUnkTerminalFilter = NULL; LOG((MSP_TRACE, "CMediaTerminal::FinalRelease succeeded")); } // we only have a destructor with debug bits #ifdef DEBUG // free the allocated member variables // virtual CMediaTerminal::~CMediaTerminal( ) { LOG((MSP_TRACE, "CMediaTerminal::~CMediaTerminal called")); } #endif // DEBUG // point to the m_ppTapiMediaType, // copies friendly name into m_szName void CMediaTerminal::SetMemberInfo( IN DWORD dwFriendlyName, IN long lMediaType ) { LOG((MSP_TRACE, "CMediaTerminal::SetMemberInfo(%d, &(%l)) called", \ dwFriendlyName,lMediaType)); // copy the friendly terminal name into the member name // the max number of TCHARs to copy is MAX_PATH+1 (it includes // the terminating NULL character) TCHAR szTemp[MAX_PATH]; if (::LoadString(_Module.GetResourceInstance(), dwFriendlyName, szTemp, MAX_PATH)) { lstrcpyn(m_szName, szTemp, MAX_PATH); } else { LOG((MSP_ERROR, "CMediaTerminal::SetMemberInfo (LoadString) failed")); } m_lMediaType = lMediaType; LOG((MSP_TRACE, "CMediaTerminal::SetMemberInfo(%d, &(%d)) succeeded", \ dwFriendlyName,lMediaType)); }; // uses the purpose id and the stream type to figure out the name // and terminal class id. // sets PurposeId, StreamType, m_szName, m_TerminalClassID // m_ppTapiMediaType, m_TerminalType, m_TerminalDirection HRESULT CMediaTerminal::SetNameInfo( IN long lMediaType, IN TERMINAL_DIRECTION TerminalDirection, OUT MSPID &PurposeId, OUT STREAM_TYPE &StreamType, OUT const GUID *&pAmovieMajorType ) { LOG((MSP_TRACE, "CMediaTerminal::SetNameInfo(%d, %u, %p, %p, %p) called", \ lMediaType, TerminalDirection, &PurposeId, &StreamType, pAmovieMajorType)); // // Check arguments // if ( ( TerminalDirection != TD_CAPTURE ) && ( TerminalDirection != TD_RENDER ) ) { return E_INVALIDARG; } // set the stream type // if its a capture terminal, the user has to write the samples StreamType = (TD_CAPTURE == TerminalDirection)? STREAMTYPE_WRITE : STREAMTYPE_READ; if (lMediaType == TAPIMEDIATYPE_AUDIO) { // set the PurposeId, major media type PurposeId = MSPID_PrimaryAudio; pAmovieMajorType = &MEDIATYPE_Audio; // copy the name and point to the tapi media type SetMemberInfo( gs_MSTFriendlyName[MEDIA_STREAM_TERMINAL_AUDIO][TerminalDirection], TAPIMEDIATYPE_AUDIO ); } else if (lMediaType == TAPIMEDIATYPE_VIDEO) { // set the PurposeId, major media type PurposeId = MSPID_PrimaryVideo; pAmovieMajorType = &MEDIATYPE_Video; // copy the name and point to the tapi media type SetMemberInfo( gs_MSTFriendlyName[MEDIA_STREAM_TERMINAL_VIDEO][TerminalDirection], TAPIMEDIATYPE_VIDEO ); } else { return E_INVALIDARG; } // its a dynamic terminal m_TerminalType = TT_DYNAMIC; LOG((MSP_TRACE, "CMediaTerminal::SetNameInfo[%p] (%u, %u, %p, %p, %p) succeeded", \ this, lMediaType, TerminalDirection, &PurposeId, &StreamType, pAmovieMajorType)); return S_OK; } // implement using the aggregated filter's public GetFormat method STDMETHODIMP CMediaTerminal::GetFormat( OUT AM_MEDIA_TYPE **ppmt ) { CLock lock(m_CritSec); LOG((MSP_TRACE, "CMediaTerminal::GetFormat(%p) called", ppmt)); return m_pAggTerminalFilter->GetFormat(ppmt); } // implement using the aggregated filter's public SetFormat method STDMETHODIMP CMediaTerminal::SetFormat( IN AM_MEDIA_TYPE *pmt ) { CLock lock(m_CritSec); LOG((MSP_TRACE, "CMediaTerminal::SetFormat(%p) called", pmt)); return m_pAggTerminalFilter->SetFormat(pmt); } // an IAMBufferNegotiation method - passed to our filter STDMETHODIMP CMediaTerminal::GetAllocatorProperties( OUT ALLOCATOR_PROPERTIES *pProperties ) { CLock lock(m_CritSec); LOG((MSP_TRACE, "CMediaTerminal::GetAllocatorProperties(%p) called", pProperties)); return m_pAggTerminalFilter->GetAllocatorProperties(pProperties); } // an IAMBufferNegotiation method - used to be not implemented // but now we must return S_OK to work with IP STDMETHODIMP CMediaTerminal::SuggestAllocatorProperties( IN const ALLOCATOR_PROPERTIES *pProperties ) { CLock lock(m_CritSec); LOG((MSP_TRACE, "CMediaTerminal::SuggestAllocatorProperties - enter")); HRESULT hr = m_pAggTerminalFilter->SuggestAllocatorProperties(pProperties); if ( FAILED(hr) ) { LOG((MSP_ERROR, "CMediaTerminal::SuggestAllocatorProperties - " "method on filter failed - exit 0x%08x", hr)); return hr; } LOG((MSP_TRACE, "CMediaTerminal::SuggestAllocatorProperties - exit S_OK")); return S_OK; } // since there is only one filter in this base class implementation (i.e. the two // ends of the terminal have the same media format), both of // the get and set methods are redirected to Get/Set Format STDMETHODIMP CMediaTerminal::get_MediaFormat( OUT AM_MEDIA_TYPE **ppFormat ) { CLock lock(m_CritSec); LOG((MSP_TRACE, "CMediaTerminal::get_MediaFormat(%p) called", ppFormat)); return GetFormat(ppFormat); } // cast the input format to a non-const as we know that we won't change the struct // in SetFormat (this problem exists because IAMStreamConfig::SetFormat expects a // non-const). this saves creating, copying and then destroying a struct for this call STDMETHODIMP CMediaTerminal::put_MediaFormat( IN const AM_MEDIA_TYPE *pFormat ) { CLock lock(m_CritSec); LOG((MSP_TRACE, "CMediaTerminal::put_MediaFormat(%p) called", pFormat)); return SetFormat((AM_MEDIA_TYPE *)pFormat); } HRESULT CMediaTerminal::CreateAndJoinMediaStreamFilter( ) { LOG((MSP_TRACE, "CMediaTerminal::CreateAndJoinMediaStreamFilter called")); ASSERT(m_pICreatedMediaStreamFilter == NULL); // in case of an error at any stage, no clean-up of member variables // or filter logic (JoinFilter(NULL) etc.) needs to be done as the // driving CBaseTerminal::ConnectTerminal would call DisconnectTerminal // which performs that work // create the media stream filter HRESULT hr; hr = CoCreateInstance( CLSID_MediaStreamFilter, NULL, CLSCTX_INPROC_SERVER, IID_IMediaStreamFilter, (void **)&m_pICreatedMediaStreamFilter ); BAIL_ON_FAILURE(hr); hr = m_pICreatedMediaStreamFilter->QueryInterface( IID_IBaseFilter, (void **)&m_pBaseFilter ); BAIL_ON_FAILURE(hr); // tell the aggregated filter of our media stream filter, so that // it can reject any other media stream filter if proposed m_pAggTerminalFilter->SetMediaStreamFilter(m_pICreatedMediaStreamFilter); // add the IAMMediaStream i/f of the aggregated terminal filter // to the media stream filter hr = m_pICreatedMediaStreamFilter->AddMediaStream(m_pIAMMediaStream); BAIL_ON_FAILURE(hr); LOG((MSP_TRACE, "CMediaTerminal::CreateAndJoinMediaStreamFilter succeeded")); return S_OK; } // if m_pFilter is null, return error // add m_pFilter to graph HRESULT CMediaTerminal::AddFiltersToGraph( ) { LOG((MSP_TRACE, "CMediaTerminal::AddFiltersToGraph called")); HRESULT hr; hr = CreateAndJoinMediaStreamFilter(); BAIL_ON_FAILURE(hr); ASSERT(m_pGraph != NULL); BAIL_IF_NULL(m_pBaseFilter, MS_E_NOTINIT); try { USES_CONVERSION; hr = m_pGraph->AddFilter(m_pBaseFilter, T2CW(m_szName)); } catch (...) { LOG((MSP_ERROR, "CMediaTerminal::AddFiltersToGraph - T2CW threw an exception - " "return E_OUTOFMEMORY")); return E_OUTOFMEMORY; } if ( ( hr != S_OK ) && ( VFW_S_DUPLICATE_NAME != hr ) ) { return hr; } LOG((MSP_TRACE, "CMediaTerminal::AddFiltersToGraph succeeded")); return S_OK; } // if m_pFilter is null, return success // remove m_pFilter from graph HRESULT CMediaTerminal::RemoveFiltersFromGraph( ) { LOG((MSP_TRACE, "CMediaTerminal::RemoveFiltersFromGraph called")); // the base filter is set when CreateAndJoinMediaStreamFilter succeeds // in it, the media stream filter is created and the IAMMediaStream // interface is added to the filter. During addition, the media stream filter // calls JoinFilter on the IAMMediaStream and thus sets the m_pBaseFilter HRESULT hr = S_OK; if ((m_pGraph != NULL) && (m_pBaseFilter != NULL)) { hr = m_pGraph->RemoveFilter(m_pBaseFilter); } // inform the aggregate media terminal filter that we don't have a // media stream filter any longer m_pAggTerminalFilter->SetMediaStreamFilter(NULL); // remove associated properties of the media stream filter m_pIAMMediaStream->JoinFilter(NULL); m_pIAMMediaStream->JoinFilterGraph(NULL); // null m_pBaseFilter and m_pICreatedMediaStreamFilter // which hold the last reference to the filter m_pBaseFilter = NULL; m_pICreatedMediaStreamFilter = NULL; return hr; } // eof