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

940 lines
21 KiB
C++

// MultiTrackTerminal.cpp: implementation of the CMultiTrackTerminal class.
//
//////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "MultiTrackTerminal.h"
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
CMultiTrackTerminal::CMultiTrackTerminal()
:m_nNumberOfTracks(0)
{
LOG((MSP_TRACE,
"CMultiTrackTerminal::CMultiTrackTerminal[%p] - enter", this));
LOG((MSP_TRACE,
"CMultiTrackTerminal::CMultiTrackTerminal - finish"));
}
CMultiTrackTerminal::~CMultiTrackTerminal()
{
LOG((MSP_TRACE,
"CMultiTrackTerminal::~CMultiTrackTerminal - enter"));
ReleaseAllTracks();
//
// we should have no tracks at this point, and counter should be in sync
//
TM_ASSERT(m_nNumberOfTracks == 0);
LOG((MSP_TRACE,
"CMultiTrackTerminal::~CMultiTrackTerminal - finish"));
}
HRESULT CMultiTrackTerminal::get_TrackTerminals(OUT VARIANT *pVariant)
{
LOG((MSP_TRACE, "CMultiTrackTerminal::get_TrackTerminals[%p] - enter. pVariant [%p]", this, pVariant));
//
// Check parameters
//
if ( IsBadWritePtr(pVariant, sizeof(VARIANT) ) )
{
LOG((MSP_ERROR, "CMultiTrackTerminal::get_TrackTerminals - "
"bad pointer argument - exit E_POINTER"));
return E_POINTER;
}
//
// the caller needs to provide us with an empty variant
//
if (pVariant->vt != VT_EMPTY)
{
LOG((MSP_ERROR, "CMultiTrackTerminal::get_TrackTerminals - "
"variant argument is not empty"));
return E_UNEXPECTED;
}
//
// create the collection object - see mspbase\mspcoll.h
//
HRESULT hr = S_OK;
typedef CTapiIfCollection<ITTerminal*> TerminalCollection;
CComObject<TerminalCollection> *pCollection = NULL;
hr = CComObject<TerminalCollection>::CreateInstance(&pCollection);
if ( FAILED(hr) )
{
LOG((MSP_ERROR, "CMultiTrackTerminal::get_TrackTerminals - "
"can't create collection - exit %lx", hr));
return hr;
}
//
// get the Collection's IDispatch interface
//
IDispatch *pDispatch = NULL;
hr = pCollection->QueryInterface(IID_IDispatch,
(void **) &pDispatch );
if ( FAILED(hr) )
{
LOG((MSP_ERROR, "CMultiTrackTerminal::get_TrackTerminals - "
"QI for IDispatch on collection failed - exit %lx", hr));
delete pCollection;
return hr;
}
{
//
// access data member array in a lock
//
CLock lock(m_lock);
//
// Init the collection using an iterator -- pointers to the beginning and
// the ending element plus one.
//
hr = pCollection->Initialize( m_TrackTerminals.GetSize(),
m_TrackTerminals.GetData(),
m_TrackTerminals.GetData() + m_TrackTerminals.GetSize() );
}
if (FAILED(hr))
{
LOG((MSP_ERROR, "CMultiTrackTerminal::get_TrackTerminals - "
"Initialize on collection failed - exit %lx", hr));
pDispatch->Release();
delete pCollection;
return hr;
}
//
// put the IDispatch interface pointer into the variant
//
LOG((MSP_ERROR, "CMultiTrackTerminal::get_TrackTerminals - "
"placing IDispatch value %p in variant", pDispatch));
VariantInit(pVariant);
pVariant->vt = VT_DISPATCH;
pVariant->pdispVal = pDispatch;
LOG((MSP_TRACE, "CMultiTrackTerminal::get_TrackTerminals - exit S_OK"));
return S_OK;
}
HRESULT CMultiTrackTerminal::EnumerateTrackTerminals(
IEnumTerminal **ppEnumTerminal
)
{
LOG((MSP_TRACE,
"CMultiTrackTerminal::EnumerateTrackTerminals entered. ppEnumTerminal[%p]", ppEnumTerminal));
//
// check arguments
//
if (IsBadWritePtr(ppEnumTerminal, sizeof(IEnumTerminal*)))
{
LOG((MSP_ERROR, "CMultiTrackTerminal::EnumerateTrackTerminals ppEnumTerminal is a bad pointer"));
return E_POINTER;
}
//
// don't return garbage
//
*ppEnumTerminal = NULL;
typedef _CopyInterface<ITTerminal> CCopy;
typedef CSafeComEnum<IEnumTerminal, &IID_IEnumTerminal,
ITTerminal *, CCopy> CEnumerator;
HRESULT hr = S_OK;
//
// create enumeration object
//
CMSPComObject<CEnumerator> *pEnum = NULL;
hr = CMSPComObject<CEnumerator>::CreateInstance(&pEnum);
if (FAILED(hr))
{
LOG((MSP_ERROR, "CMultiTrackTerminal::EnumerateTrackTerminals Could not create enumerator object, %x", hr));
return hr;
}
//
// get pEnum's IID_IEnumTerminal interface
//
hr = pEnum->QueryInterface(IID_IEnumTerminal, (void**)ppEnumTerminal);
if (FAILED(hr))
{
LOG((MSP_ERROR, "CMultiTrackTerminal::EnumerateTrackTerminals query enum interface failed, %x", hr));
*ppEnumTerminal = NULL;
//
// don't yet have outstanding reference count on pEnum, so delete it.
//
// note: this can lead to a problem if FinalRelease of pEnum is
// supposed to deallocate resources that have been allocated in its
// constructor
//
delete pEnum;
return hr;
}
//
// access data member track terminal list from a lock
//
{
CLock lock(m_lock);
// The CSafeComEnum can handle zero-sized array.
hr = pEnum->Init(
m_TrackTerminals.GetData(), // the begin itor
m_TrackTerminals.GetData() + m_TrackTerminals.GetSize(), // the end itor,
NULL, // IUnknown
AtlFlagCopy // copy the data.
);
}
if (FAILED(hr))
{
LOG((MSP_ERROR, "CMultiTrackTerminal::EnumerateTrackTerminals init enumerator object failed, %x", hr));
(*ppEnumTerminal)->Release();
*ppEnumTerminal= NULL;
return hr;
}
LOG((MSP_TRACE, "CMultiTrackTerminal::EnumerateTrackTerminals - exit S_OK"));
return hr;
}
HRESULT CMultiTrackTerminal::get_MediaTypesInUse(
OUT long *plMediaTypesInUse
)
{
LOG((MSP_TRACE, "CMultiTrackTerminal::get_MediaTypesInUse - enter. "
"plMediaTypesInUse [%p]", plMediaTypesInUse));
if (IsBadWritePtr(plMediaTypesInUse, sizeof(long)))
{
LOG((MSP_ERROR,
"CMultiTrackTerminal::get_MediaTypesInUse plMediaTypesInUse "
"does not point to a valid long"));
return E_POINTER;
}
//
// enumerate all the terminal and OR their media types and media types in use
//
long lMediaTypesInUse = 0;
//
// access data member array in a lock
//
CLock lock(m_lock);
for ( int i = 0; i < m_TrackTerminals.GetSize(); i++ )
{
long lMT = 0;
//
// is the track terminal a multitrack terminal itself?
//
ITMultiTrackTerminal *pMTT = NULL;
HRESULT hr = m_TrackTerminals[i]->QueryInterface(IID_ITMultiTrackTerminal,
(void**)&pMTT);
if (SUCCEEDED(hr))
{
//
// this is a multitrack terminal. get its mediatypes in use
//
hr = pMTT->get_MediaTypesInUse(&lMT);
pMTT->Release();
pMTT = NULL;
if (FAILED(hr))
{
//
// failed to get track's media types in use.
// continue to the next track
//
LOG((MSP_ERROR,
"CMultiTrackTerminal::get_MediaTypesInUse "
"get_MediaTypesInUse on terminal (%d) failed.", i));
continue;
}
}
else
{
//
// the track is not a multitrack terminal, so use its ITTerminal
// interface to get its media type
//
hr = m_TrackTerminals[i]->get_MediaType(&lMT);
if (FAILED(hr))
{
//
// failed to get track's media types in use.
// continue to the next track
//
LOG((MSP_ERROR,
"CMultiTrackTerminal::get_MediaTypesInUse "
"get_MediaType on terminal (%d) failed.", i));
continue;
}
}
LOG((MSP_TRACE,
"CMultiTrackTerminal::get_MediaTypesInUse "
"track terminal (%d) has media type of %lx.", i, lMT));
lMediaTypesInUse |= lMT;
}
*plMediaTypesInUse = lMediaTypesInUse;
LOG((MSP_TRACE, "CMultiTrackTerminal::get_EnumerateTrackTerminals - "
"exit S_OK. MediaTypeInUse %lx", lMediaTypesInUse));
return S_OK;
}
HRESULT CMultiTrackTerminal::get_DirectionsInUse(
OUT TERMINAL_DIRECTION *ptdDirectionsInUse
)
{
LOG((MSP_TRACE, "CMultiTrackTerminal::get_DirectionsInUse - enter. plDirectionsInUsed[%p]", ptdDirectionsInUse));
if (IsBadWritePtr(ptdDirectionsInUse, sizeof(TERMINAL_DIRECTION)))
{
LOG((MSP_ERROR,
"CMultiTrackTerminal::get_DirectionsInUse plDirectionsInUsed"
"does not point to a valid long"));
return E_POINTER;
}
//
// don't return gardbage
//
*ptdDirectionsInUse = TD_NONE;
//
// enumerate all the terminal and OR their media types and media types in use
//
TERMINAL_DIRECTION tdDirInUse = TD_NONE;
//
// access data member array in a lock
//
CLock lock(m_lock);
for ( int i = 0; i < m_TrackTerminals.GetSize(); i++ )
{
TERMINAL_DIRECTION td = TD_NONE;
//
// is the track terminal a multitrack terminal itself?
//
ITMultiTrackTerminal *pMTT = NULL;
HRESULT hr = m_TrackTerminals[i]->QueryInterface(IID_ITMultiTrackTerminal,
(void**)&pMTT);
if (SUCCEEDED(hr))
{
//
// this is a multitrack terminal. get its mediatypes in use
//
hr = pMTT->get_DirectionsInUse(&td);
pMTT->Release();
pMTT = NULL;
if (FAILED(hr))
{
//
// failed to get track's media types in use.
// continue to the next track
//
LOG((MSP_ERROR,
"CMultiTrackTerminal::get_DirectionsInUse "
"get_MediaTypesInUse on terminal (%d) failed.", i));
continue;
}
}
else
{
//
// the track is not a multitrack terminal, so use its ITTerminal
// interface to get its direction
//
hr = m_TrackTerminals[i]->get_Direction(&td);
if (FAILED(hr))
{
//
// failed to get track's media types in use.
// continue to the next track
//
LOG((MSP_ERROR,
"CMultiTrackTerminal::get_DirectionsInUse "
"get_MediaType on terminal (%d) failed.", i));
continue;
}
}
LOG((MSP_TRACE,
"CMultiTrackTerminal::get_DirectionsInUse "
"track terminal (%d) has media type of %lx.", i, td));
//
// based on directions we have collected so far, and on the direction that we just got, calculate total direction
//
switch (tdDirInUse)
{
case TD_NONE:
tdDirInUse = td;
break;
case TD_RENDER:
if ( (td != TD_RENDER) && (td != TD_NONE) )
{
tdDirInUse = TD_MULTITRACK_MIXED;
}
break;
case TD_CAPTURE:
if ( (td != TD_CAPTURE) && (td != TD_NONE) )
{
tdDirInUse = TD_MULTITRACK_MIXED;
}
break;
} // switch
if ( TD_MULTITRACK_MIXED == tdDirInUse )
{
//
// if the current direction is mixed, then break -- there is no point in looking further
//
break;
}
} // for (track terminals)
*ptdDirectionsInUse = tdDirInUse;
LOG((MSP_TRACE, "CMultiTrackTerminal::get_DirectionsInUse - exit S_OK. "
"plDirectionsInUsed = %lx", *ptdDirectionsInUse));
return S_OK;
}
///////////////////////////////////////////////////////
//
// CMultiTrackTerminal::AddTrackTerminal
//
// adds the terminal that is passed in as the argument to the
// list of track terminals managed by this multitrack terminal
//
// Note: this function increments refcount of the terminal that is being added to the list
//
HRESULT CMultiTrackTerminal::AddTrackTerminal(ITTerminal *pTrackTerminalToAdd)
{
LOG((MSP_TRACE, "CMultiTrackTerminal::AddTrackTerminal[%p] - enter. "
"pTrackTerminalToAdd = %p", this, pTrackTerminalToAdd));
if (IsBadReadPtr(pTrackTerminalToAdd, sizeof(ITTerminal*)))
{
LOG((MSP_TRACE, "CMultiTrackTerminal::AddTrackTerminal - invalid ptr"));
return E_POINTER;
}
{
//
// access data member array in a lock
//
CLock lock(m_lock);
//
// we use a special lock to increment track counter, to avoid deadlocks
// on reference counting
//
Lock();
//
// add track terminal to the array
//
if (!m_TrackTerminals.Add(pTrackTerminalToAdd))
{
LOG((MSP_ERROR, "CMultiTrackTerminal::AddTrackTerminal - "
"failed to add track to the array of terminals"));
return E_OUTOFMEMORY;
}
m_nNumberOfTracks++;
//
// the counter should never ever go out of sync
//
TM_ASSERT(m_nNumberOfTracks == m_TrackTerminals.GetSize());
Unlock();
}
//
// we are keeping a reference to the terminal, so increment refcount
//
pTrackTerminalToAdd->AddRef();
LOG((MSP_TRACE, "CMultiTrackTerminal::AddTrackTerminal - finished"));
return S_OK;
}
///////////////////////////////////////////////////////
//
// CMultiTrackTerminal::RemoveTrackTerminal
//
// removes the terminal from the list of track terminals
// managed by this multitrack terminal
//
// if success, decrementing refcount on the track terminal
//
HRESULT CMultiTrackTerminal::RemoveTrackTerminal(ITTerminal *pTrackTerminalToRemove)
{
LOG((MSP_TRACE, "CMultiTrackTerminal::RemoveTrackTerminal[%p] - enter"
"pTrackTerminalToRemove = %p", this, pTrackTerminalToRemove));
{
//
// access data member array in a lock
//
CLock lock(m_lock);
//
// decrement track counter in a special lock to prevent deadlocks
// with reference counting
//
Lock();
//
// remove track from the array
//
if (!m_TrackTerminals.Remove(pTrackTerminalToRemove))
{
LOG((MSP_ERROR, "CMultiTrackTerminal::RemoveTrackTerminal - "
"failed to remove from the array of terminals"));
return E_INVALIDARG;
}
m_nNumberOfTracks--;
//
// the counter should never ever go out of sync
//
TM_ASSERT(m_nNumberOfTracks == m_TrackTerminals.GetSize());
Unlock();
}
//
// we are releasing a reference to the terminal, so decrement refcount
//
pTrackTerminalToRemove->Release();
LOG((MSP_TRACE, "CMultiTrackTerminal::RemoveTrackTerminal- finished"));
return S_OK;
}
///////////////////////////////////////////////////////
//
// CMultiTrackTerminal::ReleaseAllTracks
//
// removes all tracks from the list of managed track terminals
// and Release's them
//
//
HRESULT CMultiTrackTerminal::ReleaseAllTracks()
{
LOG((MSP_TRACE, "CMultiTrackTerminal::ReleaseAllTracks[%p] - enter", this));
{
//
// access data member array in a lock
//
CLock lock(m_lock);
int nNumberOfTerminalsInArray = m_TrackTerminals.GetSize();
for (int i = 0; i < nNumberOfTerminalsInArray; i++)
{
//
// release and remove the first terminal in the array
//
LOG((MSP_TRACE, "CMultiTrackTerminal::ReleaseAllTracks - releasing track [%p]", m_TrackTerminals[0]));
m_TrackTerminals[0]->Release();
//
// remove element from the array and decrement track counter in a
// special lock to prevent deadlocks with reference counting
//
Lock();
m_TrackTerminals.RemoveAt(0);
m_nNumberOfTracks--;
//
// the counter should never ever go out of sync
//
TM_ASSERT(m_nNumberOfTracks == m_TrackTerminals.GetSize());
Unlock();
}
//
// we should have cleared the array
//
TM_ASSERT(0 == m_TrackTerminals.GetSize());
}
LOG((MSP_TRACE, "CMultiTrackTerminal::ReleaseAllTracks - finished"));
return S_OK;
}
/////////////////////////////////////////////////////////////////////////////
//
// CMultiTrackTerminal::InternalAddRef
//
// keep track of refcount.
//
// we need to adjust refcount with the information on the number of tracks
// that we are managing.
//
ULONG CMultiTrackTerminal::InternalAddRef()
{
// LOG((MSP_TRACE, "CMultiTrackTerminal::InternalAddRef[%p] - enter.", this));
LONG lReturnValue = InterlockedIncrement(&m_dwRef);
lReturnValue -= CountTracks();
// LOG((MSP_TRACE, "CMultiTrackTerminal::InternalAddRef - finish. returning %ld", lReturnValue));
return lReturnValue;
}
/////////////////////////////////////////////////////////////////////////////
//
// CMultiTrackTerminal::InternalRelease
//
// keep track of refcount.
// return 0 when there are no outstanding references to me or my children
//
ULONG CMultiTrackTerminal::InternalRelease()
{
// LOG((MSP_TRACE, "CMultiTrackTerminal::InternalRelease[%p] - enter", this));
LONG lReturnValue = InterlockedDecrement(&m_dwRef);
lReturnValue -= CountTracks();
// LOG((MSP_TRACE, "CMultiTrackTerminal::InternalRelease - finish. returning %ld", lReturnValue));
return lReturnValue;
}
//////////////////////////////////////////////////////////////////////
//
// CMultiTrackTerminal::ChildAddRef
//
// this method is called by a track terminal when it is AddRef'd,
// so the File Rec terminal can keep track of its children's refcounts
//
void CMultiTrackTerminal::ChildAddRef()
{
// LOG((MSP_TRACE, "CMultiTrackTerminal::ChildAddRef[%p] - enter.", this));
AddRef();
// LOG((MSP_TRACE, "CMultiTrackTerminal::ChildAddRef - finish."));
}
//////////////////////////////////////////////////////////////////////
//
// CMultiTrackTerminal::ChildRelease
//
// this method is called by a track terminal when it is released,
// so the File Rec terminal can keep track of its children's refcounts
//
void CMultiTrackTerminal::ChildRelease()
{
// LOG((MSP_TRACE, "CMultiTrackTerminal::ChildRelease[%p] - enter.", this));
Release();
// LOG((MSP_TRACE, "CMultiTrackTerminal::ChildRelease - finish."));
}
//////////////////////////////////////////////////////////////////////
//
// CMultiTrackTerminal::CountTracks
//
// this method returns the number of tracks managed by this parent
//
int CMultiTrackTerminal::CountTracks()
{
// LOG((MSP_TRACE, "CMultiTrackTerminal::CountTracks[%p] - enter", this));
//
// this lock is only used to protect accesses to this var. this is
// needed to prevent deadlocks when
//
// one thread locks the parent
// terminal and enumerates the tracks (thus getting their locks)
//
// and
//
// another thread addrefs or releases a track. this locks the
// track and attempts to notify the parent of the child's refcount
// change. if this thread tries to lock the parent, we would have a
// deadlock
//
// so instead of locking the parent on addref and release, we only use
// this "addref/release" lock
//
Lock();
int nNumberOfTracks = m_nNumberOfTracks;
Unlock();
// LOG((MSP_TRACE, "CMultiTrackTerminal::CountTracks - finished. NumberOfTracks = %d", nNumberOfTracks));
return nNumberOfTracks;
}