// 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 TerminalCollection; CComObject *pCollection = NULL; hr = CComObject::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 CCopy; typedef CSafeComEnum CEnumerator; HRESULT hr = S_OK; // // create enumeration object // CMSPComObject *pEnum = NULL; hr = CMSPComObject::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; }