You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
609 lines
17 KiB
609 lines
17 KiB
/*
|
|
|
|
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
|