//==========================================================================; // // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY // KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE // IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR // PURPOSE. // // Copyright (c) 1992 - 1998 Microsoft Corporation. All Rights Reserved. // //--------------------------------------------------------------------------; #include #include #include "inftee.h" #include #include // // // What this sample illustrates // // A pass through filter splits a data stream into many output channels // // Summary // // This is a sample ActiveMovie pass through filter. We have a single input // pin and can have many output pins. We start with one output pin and each // time we connect an output pin we spawn another, although we could keep // doing this ad infinitum we have a top level maximum of INFTEE_MAX_PINS. // Any data samples our input pin receives will be sent down each output // pin in turn. Each output pin has a separate thread if necessary (see // the output queue class in the SDK) to avoid delivery blocking our thread // // Demonstration instructions // // Start GRAPHEDT available in the ActiveMovie SDK tools. Drag and drop any // MPEG, AVI or MOV file into the tool and it will be rendered. Then go to // the filters in the graph and find the filter (box) titled "Video Renderer" // Then click on the box and hit DELETE. After that go to the Graph menu and // select "Insert Filters", from the dialog box find and select the "Infinite // Tee Filter" and then dismiss the dialog. Back in the graph layout find the // output pin of the filter that was connected to the input of the video // renderer you just deleted, right click and select "Render". You should // see it being connected to the input pin of the filter you just inserted // // The infinite tee filter will have one output pin connected and will have // spawned another, right click on this and select Render. A new renderer // will pop up fo the stream. Do this once or twice more and then click on // the Pause and Run on the GRAPHEDT frame and you will see the video... // .. many times over in different windows // // Files // // inftee.cpp Main implementation of the infinite tee // inftee.def What APIs the DLL will import and export // inftee.h Class definition of the infinite tee // inftee.rc Not much, just our version information // makefile How to build it... // // // Base classes used // // CBaseInputPin Basic IMemInputPin based input pin // CBaseOutputPin Used for basic connection stuff // CBaseFilter Well we need a filter don't we // CCritSec Controls access to output pin list // COutputQueue Delivers data on a separate thread // // #define INFTEE_MAX_PINS 1000 // Using this pointer in constructor #pragma warning(disable:4355) // Setup data const AMOVIESETUP_MEDIATYPE sudPinTypes = { &MEDIATYPE_NULL, // Major CLSID &MEDIASUBTYPE_NULL // Minor type }; const AMOVIESETUP_PIN psudPins[] = { { L"Input", // Pin's string name FALSE, // Is it rendered FALSE, // Is it an output FALSE, // Allowed none FALSE, // Allowed many &CLSID_NULL, // Connects to filter L"Output", // Connects to pin 1, // Number of types &sudPinTypes }, // Pin information { L"Output", // Pin's string name FALSE, // Is it rendered TRUE, // Is it an output FALSE, // Allowed none FALSE, // Allowed many &CLSID_NULL, // Connects to filter L"Input", // Connects to pin 1, // Number of types &sudPinTypes } // Pin information }; const AMOVIESETUP_FILTER sudInfTee = { &CLSID_Tee, // CLSID of filter L"Sample Infinite Pin Tee", // Filter's name MERIT_DO_NOT_USE, // Filter merit 2, // Number of pins psudPins // Pin information }; CFactoryTemplate g_Templates [1] = { { L"Infinite Pin Tee" , &CLSID_Tee , CTee::CreateInstance , NULL , &sudInfTee } }; int g_cTemplates = sizeof(g_Templates) / sizeof(g_Templates[0]); // // CreateInstance // // Creator function for the class ID // CUnknown * WINAPI CTee::CreateInstance(LPUNKNOWN pUnk, HRESULT *phr) { return new CTee(NAME("Infinite Tee Filter"), pUnk, phr); } // // Constructor // CTee::CTee(TCHAR *pName, LPUNKNOWN pUnk, HRESULT *phr) : m_OutputPinsList(NAME("Tee Output Pins list")), m_lCanSeek(TRUE), m_pAllocator(NULL), m_NumOutputPins(0), m_NextOutputPinNumber(0), m_Input(NAME("Input Pin"), this, phr, L"Input"), CBaseFilter(NAME("Tee filter"), pUnk, this, CLSID_Tee) { ASSERT(phr); // Create a single output pin at this time InitOutputPinsList(); CTeeOutputPin *pOutputPin = CreateNextOutputPin(this); if (pOutputPin != NULL ) { m_NumOutputPins++; m_OutputPinsList.AddTail(pOutputPin); } } // // Destructor // CTee::~CTee() { InitOutputPinsList(); } // // GetPinCount // int CTee::GetPinCount() { return (1 + m_NumOutputPins); } // // GetPin // CBasePin *CTee::GetPin(int n) { if (n < 0) return NULL ; // Pin zero is the one and only input pin if (n == 0) return &m_Input; // return the output pin at position(n - 1) (zero based) return GetPinNFromList(n - 1); } // // InitOutputPinsList // void CTee::InitOutputPinsList() { POSITION pos = m_OutputPinsList.GetHeadPosition(); while(pos) { CTeeOutputPin *pOutputPin = m_OutputPinsList.GetNext(pos); ASSERT(pOutputPin->m_pOutputQueue == NULL); pOutputPin->Release(); } m_NumOutputPins = 0; m_OutputPinsList.RemoveAll(); } // InitOutputPinsList // // CreateNextOutputPin // CTeeOutputPin *CTee::CreateNextOutputPin(CTee *pTee) { WCHAR szbuf[20]; // Temporary scratch buffer m_NextOutputPinNumber++; // Next number to use for pin HRESULT hr = NOERROR; swprintf(szbuf, L"Output%d", m_NextOutputPinNumber); CTeeOutputPin *pPin = new CTeeOutputPin(NAME("Tee Output"), pTee, &hr, szbuf, m_NextOutputPinNumber); if (FAILED(hr) || pPin == NULL) { delete pPin; return NULL; } pPin->AddRef(); return pPin; } // CreateNextOutputPin // // DeleteOutputPin // void CTee::DeleteOutputPin(CTeeOutputPin *pPin) { POSITION pos = m_OutputPinsList.GetHeadPosition(); while(pos) { POSITION posold = pos; // Remember this position CTeeOutputPin *pOutputPin = m_OutputPinsList.GetNext(pos); if (pOutputPin == pPin) { // If this pin holds the seek interface release it if (pPin->m_bHoldsSeek) { InterlockedExchange(&m_lCanSeek, FALSE); pPin->m_bHoldsSeek = FALSE; pPin->m_pPosition->Release(); } m_OutputPinsList.Remove(posold); ASSERT(pOutputPin->m_pOutputQueue == NULL); delete pPin; m_NumOutputPins--; IncrementPinVersion(); break; } } } // DeleteOutputPin // // GetNumFreePins // int CTee::GetNumFreePins() { int n = 0; POSITION pos = m_OutputPinsList.GetHeadPosition(); while(pos) { CTeeOutputPin *pOutputPin = m_OutputPinsList.GetNext(pos); if (pOutputPin->m_Connected == NULL) n++; } return n; } // GetNumFreePins // // GetPinNFromList // CTeeOutputPin *CTee::GetPinNFromList(int n) { // Validate the position being asked for if (n >= m_NumOutputPins) return NULL; // Get the head of the list POSITION pos = m_OutputPinsList.GetHeadPosition(); n++; // Make the number 1 based CTeeOutputPin *pOutputPin; while(n) { pOutputPin = m_OutputPinsList.GetNext(pos); n--; } return pOutputPin; } // GetPinNFromList // // Stop // // Overriden to handle no input connections // STDMETHODIMP CTee::Stop() { CBaseFilter::Stop(); m_State = State_Stopped; return NOERROR; } // // Pause // // Overriden to handle no input connections // STDMETHODIMP CTee::Pause() { CAutoLock cObjectLock(m_pLock); HRESULT hr = CBaseFilter::Pause(); if (m_Input.IsConnected() == FALSE) { m_Input.EndOfStream(); } return hr; } // // Run // // Overriden to handle no input connections // STDMETHODIMP CTee::Run(REFERENCE_TIME tStart) { CAutoLock cObjectLock(m_pLock); HRESULT hr = CBaseFilter::Run(tStart); if (m_Input.IsConnected() == FALSE) { m_Input.EndOfStream(); } return hr; } // // CTeeInputPin constructor // CTeeInputPin::CTeeInputPin(TCHAR *pName, CTee *pTee, HRESULT *phr, LPCWSTR pPinName) : CBaseInputPin(pName, pTee, pTee, phr, pPinName), m_pTee(pTee), m_bInsideCheckMediaType(FALSE) { ASSERT(pTee); } #ifdef DEBUG // // CTeeInputPin destructor // CTeeInputPin::~CTeeInputPin() { DbgLog((LOG_TRACE,2,TEXT("CTeeInputPin destructor"))); ASSERT(m_pTee->m_pAllocator == NULL); } #endif #ifdef DEBUG // // DisplayMediaType -- (DEBUG ONLY) // void DisplayMediaType(TCHAR *pDescription,const CMediaType *pmt) { // Dump the GUID types and a short description DbgLog((LOG_TRACE,2,TEXT(""))); DbgLog((LOG_TRACE,2,TEXT("%s"),pDescription)); DbgLog((LOG_TRACE,2,TEXT(""))); DbgLog((LOG_TRACE,2,TEXT("Media Type Description"))); DbgLog((LOG_TRACE,2,TEXT("Major type %s"),GuidNames[*pmt->Type()])); DbgLog((LOG_TRACE,2,TEXT("Subtype %s"),GuidNames[*pmt->Subtype()])); DbgLog((LOG_TRACE,2,TEXT("Subtype description %s"),GetSubtypeName(pmt->Subtype()))); DbgLog((LOG_TRACE,2,TEXT("Format size %d"),pmt->cbFormat)); // Dump the generic media types */ DbgLog((LOG_TRACE,2,TEXT("Fixed size sample %d"),pmt->IsFixedSize())); DbgLog((LOG_TRACE,2,TEXT("Temporal compression %d"),pmt->IsTemporalCompressed())); DbgLog((LOG_TRACE,2,TEXT("Sample size %d"),pmt->GetSampleSize())); } // DisplayMediaType #endif // // CheckMediaType // HRESULT CTeeInputPin::CheckMediaType(const CMediaType *pmt) { CAutoLock lock_it(m_pLock); // If we are already inside checkmedia type for this pin, return NOERROR // It is possble to hookup two of the tee filters and some other filter // like the video effects sample to get into this situation. If we don't // detect this situation, we will carry on looping till we blow the stack if (m_bInsideCheckMediaType == TRUE) return NOERROR; m_bInsideCheckMediaType = TRUE; HRESULT hr = NOERROR; #ifdef DEBUG // Display the type of the media for debugging perposes DisplayMediaType(TEXT("Input Pin Checking"), pmt); #endif // The media types that we can support are entirely dependent on the // downstream connections. If we have downstream connections, we should // check with them - walk through the list calling each output pin int n = m_pTee->m_NumOutputPins; POSITION pos = m_pTee->m_OutputPinsList.GetHeadPosition(); while(n) { CTeeOutputPin *pOutputPin = m_pTee->m_OutputPinsList.GetNext(pos); if (pOutputPin != NULL) { if (pOutputPin->m_Connected != NULL) { // The pin is connected, check its peer hr = pOutputPin->m_Connected->QueryAccept(pmt); if (hr != NOERROR) { m_bInsideCheckMediaType = FALSE; return VFW_E_TYPE_NOT_ACCEPTED; } } } else { // We should have as many pins as the count says we have ASSERT(FALSE); } n--; } // Either all the downstream pins have accepted or there are none. m_bInsideCheckMediaType = FALSE; return NOERROR; } // CheckMediaType // // SetMediaType // HRESULT CTeeInputPin::SetMediaType(const CMediaType *pmt) { CAutoLock lock_it(m_pLock); HRESULT hr = NOERROR; // Make sure that the base class likes it hr = CBaseInputPin::SetMediaType(pmt); if (FAILED(hr)) return hr; ASSERT(m_Connected != NULL); return NOERROR; } // SetMediaType // // BreakConnect // HRESULT CTeeInputPin::BreakConnect() { // Release any allocator that we are holding if (m_pTee->m_pAllocator) { m_pTee->m_pAllocator->Release(); m_pTee->m_pAllocator = NULL; } return NOERROR; } // BreakConnect // // NotifyAllocator // STDMETHODIMP CTeeInputPin::NotifyAllocator(IMemAllocator *pAllocator, BOOL bReadOnly) { CAutoLock lock_it(m_pLock); if (pAllocator == NULL) return E_FAIL; // Free the old allocator if any if (m_pTee->m_pAllocator) m_pTee->m_pAllocator->Release(); // Store away the new allocator pAllocator->AddRef(); m_pTee->m_pAllocator = pAllocator; // Notify the base class about the allocator return CBaseInputPin::NotifyAllocator(pAllocator,bReadOnly); } // NotifyAllocator // // EndOfStream // HRESULT CTeeInputPin::EndOfStream() { CAutoLock lock_it(m_pLock); ASSERT(m_pTee->m_NumOutputPins); HRESULT hr = NOERROR; // Walk through the output pins list, sending the message downstream int n = m_pTee->m_NumOutputPins; POSITION pos = m_pTee->m_OutputPinsList.GetHeadPosition(); while(n) { CTeeOutputPin *pOutputPin = m_pTee->m_OutputPinsList.GetNext(pos); if (pOutputPin != NULL) { hr = pOutputPin->DeliverEndOfStream(); if (FAILED(hr)) return hr; } else { // We should have as many pins as the count says we have ASSERT(FALSE); } n--; } return(NOERROR); } // EndOfStream // // BeginFlush // HRESULT CTeeInputPin::BeginFlush() { CAutoLock lock_it(m_pLock); ASSERT(m_pTee->m_NumOutputPins); HRESULT hr = NOERROR; // Walk through the output pins list, sending the message downstream int n = m_pTee->m_NumOutputPins; POSITION pos = m_pTee->m_OutputPinsList.GetHeadPosition(); while(n) { CTeeOutputPin *pOutputPin = m_pTee->m_OutputPinsList.GetNext(pos); if (pOutputPin != NULL) { hr = pOutputPin->DeliverBeginFlush(); if (FAILED(hr)) return hr; } else { // We should have as many pins as the count says we have ASSERT(FALSE); } n--; } return CBaseInputPin::BeginFlush(); } // BeginFlush // // EndFlush // HRESULT CTeeInputPin::EndFlush() { CAutoLock lock_it(m_pLock); ASSERT(m_pTee->m_NumOutputPins); HRESULT hr = NOERROR; // Walk through the output pins list, sending the message downstream int n = m_pTee->m_NumOutputPins; POSITION pos = m_pTee->m_OutputPinsList.GetHeadPosition(); while(n) { CTeeOutputPin *pOutputPin = m_pTee->m_OutputPinsList.GetNext(pos); if (pOutputPin != NULL) { hr = pOutputPin->DeliverEndFlush(); if (FAILED(hr)) return hr; } else { // We should have as many pins as the count says we have ASSERT(FALSE); } n--; } return CBaseInputPin::EndFlush(); } // EndFlush // // NewSegment // HRESULT CTeeInputPin::NewSegment(REFERENCE_TIME tStart, REFERENCE_TIME tStop, double dRate) { CAutoLock lock_it(m_pLock); ASSERT(m_pTee->m_NumOutputPins); HRESULT hr = NOERROR; // Walk through the output pins list, sending the message downstream int n = m_pTee->m_NumOutputPins; POSITION pos = m_pTee->m_OutputPinsList.GetHeadPosition(); while(n) { CTeeOutputPin *pOutputPin = m_pTee->m_OutputPinsList.GetNext(pos); if (pOutputPin != NULL) { hr = pOutputPin->DeliverNewSegment(tStart, tStop, dRate); if (FAILED(hr)) return hr; } else { // We should have as many pins as the count says we have ASSERT(FALSE); } n--; } return CBaseInputPin::NewSegment(tStart, tStop, dRate); } // NewSegment // // Receive // HRESULT CTeeInputPin::Receive(IMediaSample *pSample) { CAutoLock lock_it(m_pLock); // Check that all is well with the base class HRESULT hr = NOERROR; hr = CBaseInputPin::Receive(pSample); if (hr != NOERROR) return hr; // Walk through the output pins list, delivering to each in turn int n = m_pTee->m_NumOutputPins; POSITION pos = m_pTee->m_OutputPinsList.GetHeadPosition(); while(n) { CTeeOutputPin *pOutputPin = m_pTee->m_OutputPinsList.GetNext(pos); if (pOutputPin != NULL) { hr = pOutputPin->Deliver(pSample); if (hr != NOERROR) return hr; } else { // We should have as many pins as the count says we have ASSERT(FALSE); } n--; } return NOERROR; } // Receive // // Completed a connection to a pin // HRESULT CTeeInputPin::CompleteConnect(IPin *pReceivePin) { HRESULT hr = CBaseInputPin::CompleteConnect(pReceivePin); if (FAILED(hr)) { return hr; } // Force any output pins to use our type int n = m_pTee->m_NumOutputPins; POSITION pos = m_pTee->m_OutputPinsList.GetHeadPosition(); while(n) { CTeeOutputPin *pOutputPin = m_pTee->m_OutputPinsList.GetNext(pos); if (pOutputPin != NULL) { // Check with downstream pin if (pOutputPin->m_Connected != NULL) { if (m_mt != pOutputPin->m_mt) m_pTee->ReconnectPin(pOutputPin, &m_mt); } } else { // We should have as many pins as the count says we have ASSERT(FALSE); } n--; } return S_OK; } // // CTeeOutputPin constructor // CTeeOutputPin::CTeeOutputPin(TCHAR *pName, CTee *pTee, HRESULT *phr, LPCWSTR pPinName, int PinNumber) : CBaseOutputPin(pName, pTee, pTee, phr, pPinName) , m_pOutputQueue(NULL), m_bHoldsSeek(FALSE), m_pPosition(NULL), m_pTee(pTee), m_cOurRef(0), m_bInsideCheckMediaType(FALSE) { ASSERT(pTee); } #ifdef DEBUG // // CTeeOutputPin destructor // CTeeOutputPin::~CTeeOutputPin() { ASSERT(m_pOutputQueue == NULL); } #endif // // NonDelegatingQueryInterface // // This function is overwritten to expose IMediaPosition and IMediaSelection // Note that only one output stream can be allowed to expose this to avoid // conflicts, the other pins will just return E_NOINTERFACE and therefore // appear as non seekable streams. We have a LONG value that if exchanged to // produce a TRUE means that we have the honor. If it exchanges to FALSE then // someone is already in. If we do get it and error occurs then we reset it // to TRUE so someone else can get it. // STDMETHODIMP CTeeOutputPin::NonDelegatingQueryInterface(REFIID riid, void **ppv) { CheckPointer(ppv,E_POINTER); ASSERT(ppv); *ppv = NULL; HRESULT hr = NOERROR; // See what interface the caller is interested in. if (riid == IID_IMediaPosition || riid == IID_IMediaSeeking) { if (m_pPosition) { if (m_bHoldsSeek == FALSE) return E_NOINTERFACE; return m_pPosition->QueryInterface(riid, ppv); } } else return CBaseOutputPin::NonDelegatingQueryInterface(riid, ppv); CAutoLock lock_it(m_pLock); ASSERT(m_pPosition == NULL); IUnknown *pMediaPosition = NULL; // Try to create a seeking implementation if (InterlockedExchange(&m_pTee->m_lCanSeek, FALSE) == FALSE) return E_NOINTERFACE; // Create implementation of this dynamically as sometimes we may never // try and seek. The helper object implements IMediaPosition and also // the IMediaSelection control interface and simply takes the calls // normally from the downstream filter and passes them upstream hr = CreatePosPassThru( GetOwner(), FALSE, (IPin *)&m_pTee->m_Input, &pMediaPosition); if (pMediaPosition == NULL) { InterlockedExchange(&m_pTee->m_lCanSeek, TRUE); return E_OUTOFMEMORY; } if (FAILED(hr)) { InterlockedExchange(&m_pTee->m_lCanSeek, TRUE); pMediaPosition->Release (); return hr; } m_pPosition = pMediaPosition; m_bHoldsSeek = TRUE; return NonDelegatingQueryInterface(riid, ppv); } // NonDelegatingQueryInterface // // NonDelegatingAddRef // // We need override this method so that we can do proper reference counting // on our output pin. The base class CBasePin does not do any reference // counting on the pin in RETAIL. // // Please refer to the comments for the NonDelegatingRelease method for more // info on why we need to do this. // STDMETHODIMP_(ULONG) CTeeOutputPin::NonDelegatingAddRef() { CAutoLock lock_it(m_pLock); #ifdef DEBUG // Update the debug only variable maintained by the base class m_cRef++; ASSERT(m_cRef > 0); #endif // Now update our reference count m_cOurRef++; ASSERT(m_cOurRef > 0); return m_cOurRef; } // NonDelegatingAddRef // // NonDelegatingRelease // // CTeeOutputPin overrides this class so that we can take the pin out of our // output pins list and delete it when its reference count drops to 1 and there // is atleast two free pins. // // Note that CreateNextOutputPin holds a reference count on the pin so that // when the count drops to 1, we know that no one else has the pin. // // Moreover, the pin that we are about to delete must be a free pin(or else // the reference would not have dropped to 1, and we must have atleast one // other free pin(as the filter always wants to have one more free pin) // // Also, since CBasePin::NonDelegatingAddRef passes the call to the owning // filter, we will have to call Release on the owning filter as well. // // Also, note that we maintain our own reference count m_cOurRef as the m_cRef // variable maintained by CBasePin is debug only. // STDMETHODIMP_(ULONG) CTeeOutputPin::NonDelegatingRelease() { CAutoLock lock_it(m_pLock); #ifdef DEBUG // Update the debug only variable in CBasePin m_cRef--; ASSERT(m_cRef >= 0); #endif // Now update our reference count m_cOurRef--; ASSERT(m_cOurRef >= 0); // if the reference count on the object has gone to one, remove // the pin from our output pins list and physically delete it // provided there are atealst two free pins in the list(including // this one) // Also, when the ref count drops to 0, it really means that our // filter that is holding one ref count has released it so we // should delete the pin as well. if (m_cOurRef <= 1) { int n = 2; // default forces pin deletion if (m_cOurRef == 1) { // Walk the list of pins, looking for count of free pins n = m_pTee->GetNumFreePins(); } // If there are two free pins, delete this one. // NOTE: normall if (n >= 2 ) { m_cOurRef = 0; #ifdef DEBUG m_cRef = 0; #endif m_pTee->DeleteOutputPin(this); return(ULONG) 0; } } return(ULONG) m_cOurRef; } // NonDelegatingRelease // // DecideBufferSize // // This has to be present to override the PURE virtual class base function // HRESULT CTeeOutputPin::DecideBufferSize(IMemAllocator *pMemAllocator, ALLOCATOR_PROPERTIES * ppropInputRequest) { return NOERROR; } // DecideBufferSize // // DecideAllocator // HRESULT CTeeOutputPin::DecideAllocator(IMemInputPin *pPin, IMemAllocator **ppAlloc) { ASSERT(m_pTee->m_pAllocator != NULL); *ppAlloc = NULL; // Tell the pin about our allocator, set by the input pin. HRESULT hr = NOERROR; hr = pPin->NotifyAllocator(m_pTee->m_pAllocator,TRUE); if (FAILED(hr)) return hr; // Return the allocator *ppAlloc = m_pTee->m_pAllocator; m_pTee->m_pAllocator->AddRef(); return NOERROR; } // DecideAllocator // // CheckMediaType // HRESULT CTeeOutputPin::CheckMediaType(const CMediaType *pmt) { CAutoLock lock_it(m_pLock); // If we are already inside checkmedia type for this pin, return NOERROR // It is possble to hookup two of the tee filters and some other filter // like the video effects sample to get into this situation. If we // do not detect this, we will loop till we blow the stack if (m_bInsideCheckMediaType == TRUE) return NOERROR; m_bInsideCheckMediaType = TRUE; HRESULT hr = NOERROR; #ifdef DEBUG // Display the type of the media for debugging purposes DisplayMediaType(TEXT("Output Pin Checking"), pmt); #endif // The input needs to have been conneced first if (m_pTee->m_Input.m_Connected == NULL) { m_bInsideCheckMediaType = FALSE; return VFW_E_NOT_CONNECTED; } // Make sure that our input pin peer is happy with this hr = m_pTee->m_Input.m_Connected->QueryAccept(pmt); if (hr != NOERROR) { m_bInsideCheckMediaType = FALSE; return VFW_E_TYPE_NOT_ACCEPTED; } // Check the format with the other outpin pins int n = m_pTee->m_NumOutputPins; POSITION pos = m_pTee->m_OutputPinsList.GetHeadPosition(); while(n) { CTeeOutputPin *pOutputPin = m_pTee->m_OutputPinsList.GetNext(pos); if (pOutputPin != NULL && pOutputPin != this) { if (pOutputPin->m_Connected != NULL) { // The pin is connected, check its peer hr = pOutputPin->m_Connected->QueryAccept(pmt); if (hr != NOERROR) { m_bInsideCheckMediaType = FALSE; return VFW_E_TYPE_NOT_ACCEPTED; } } } n--; } m_bInsideCheckMediaType = FALSE; return NOERROR; } // CheckMediaType // // EnumMediaTypes // STDMETHODIMP CTeeOutputPin::EnumMediaTypes(IEnumMediaTypes **ppEnum) { CAutoLock lock_it(m_pLock); ASSERT(ppEnum); // Make sure that we are connected if (m_pTee->m_Input.m_Connected == NULL) return VFW_E_NOT_CONNECTED; // We will simply return the enumerator of our input pin's peer return m_pTee->m_Input.m_Connected->EnumMediaTypes(ppEnum); } // EnumMediaTypes // // SetMediaType // HRESULT CTeeOutputPin::SetMediaType(const CMediaType *pmt) { CAutoLock lock_it(m_pLock); #ifdef DEBUG // Display the format of the media for debugging purposes DisplayMediaType(TEXT("Output pin type agreed"), pmt); #endif // Make sure that we have an input connected if (m_pTee->m_Input.m_Connected == NULL) return VFW_E_NOT_CONNECTED; // Make sure that the base class likes it HRESULT hr = NOERROR; hr = CBaseOutputPin::SetMediaType(pmt); if (FAILED(hr)) return hr; return NOERROR; } // SetMediaType // // CompleteConnect // HRESULT CTeeOutputPin::CompleteConnect(IPin *pReceivePin) { CAutoLock lock_it(m_pLock); ASSERT(m_Connected == pReceivePin); HRESULT hr = NOERROR; hr = CBaseOutputPin::CompleteConnect(pReceivePin); if (FAILED(hr)) return hr; // If the type is not the same as that stored for the input // pin then force the input pins peer to be reconnected if (m_mt != m_pTee->m_Input.m_mt) { hr = m_pTee->ReconnectPin(m_pTee->m_Input.m_Connected, &m_mt); if(FAILED(hr)) { return hr; } } // Since this pin has been connected up, create another output pin. We // will do this only if there are no unconnected pins on us. However // CompleteConnect will get called for the same pin during reconnection int n = m_pTee->GetNumFreePins(); ASSERT(n <= 1); if (n == 1 || m_pTee->m_NumOutputPins == INFTEE_MAX_PINS) return NOERROR; // No unconnected pins left so spawn a new one CTeeOutputPin *pOutputPin = m_pTee->CreateNextOutputPin(m_pTee); if (pOutputPin != NULL ) { m_pTee->m_NumOutputPins++; m_pTee->m_OutputPinsList.AddTail(pOutputPin); m_pTee->IncrementPinVersion(); } // At this point we should be able to send some // notification that we have sprung a new pin return NOERROR; } // CompleteConnect // // Active // // This is called when we start running or go paused. We create the // output queue object to send data to our associated peer pin // HRESULT CTeeOutputPin::Active() { CAutoLock lock_it(m_pLock); HRESULT hr = NOERROR; // Make sure that the pin is connected if (m_Connected == NULL) return NOERROR; // Create the output queue if we have to if (m_pOutputQueue == NULL) { m_pOutputQueue = new COutputQueue(m_Connected, &hr, TRUE, FALSE); if (m_pOutputQueue == NULL) return E_OUTOFMEMORY; // Make sure that the constructor did not return any error if (FAILED(hr)) { delete m_pOutputQueue; m_pOutputQueue = NULL; return hr; } } // Pass the call on to the base class CBaseOutputPin::Active(); return NOERROR; } // Active // // Inactive // // This is called when we stop streaming // We delete the output queue at this time // HRESULT CTeeOutputPin::Inactive() { CAutoLock lock_it(m_pLock); // Delete the output queus associated with the pin. if (m_pOutputQueue) { delete m_pOutputQueue; m_pOutputQueue = NULL; } CBaseOutputPin::Inactive(); return NOERROR; } // Inactive // // Deliver // HRESULT CTeeOutputPin::Deliver(IMediaSample *pMediaSample) { // Make sure that we have an output queue if (m_pOutputQueue == NULL) return NOERROR; pMediaSample->AddRef(); return m_pOutputQueue->Receive(pMediaSample); } // Deliver // // DeliverEndOfStream // HRESULT CTeeOutputPin::DeliverEndOfStream() { // Make sure that we have an output queue if (m_pOutputQueue == NULL) return NOERROR; m_pOutputQueue->EOS(); return NOERROR; } // DeliverEndOfStream // // DeliverBeginFlush // HRESULT CTeeOutputPin::DeliverBeginFlush() { // Make sure that we have an output queue if (m_pOutputQueue == NULL) return NOERROR; m_pOutputQueue->BeginFlush(); return NOERROR; } // DeliverBeginFlush // // DeliverEndFlush // HRESULT CTeeOutputPin::DeliverEndFlush() { // Make sure that we have an output queue if (m_pOutputQueue == NULL) return NOERROR; m_pOutputQueue->EndFlush(); return NOERROR; } // DeliverEndFlish // // DeliverNewSegment // HRESULT CTeeOutputPin::DeliverNewSegment(REFERENCE_TIME tStart, REFERENCE_TIME tStop, double dRate) { // Make sure that we have an output queue if (m_pOutputQueue == NULL) return NOERROR; m_pOutputQueue->NewSegment(tStart, tStop, dRate); return NOERROR; } // DeliverNewSegment // // Notify // STDMETHODIMP CTeeOutputPin::Notify(IBaseFilter *pSender, Quality q) { // We pass the message on, which means that we find the quality sink // for our input pin and send it there POSITION pos = m_pTee->m_OutputPinsList.GetHeadPosition(); CTeeOutputPin *pFirstOutput = m_pTee->m_OutputPinsList.GetNext(pos); if (this == pFirstOutput) { if (m_pTee->m_Input.m_pQSink!=NULL) { return m_pTee->m_Input.m_pQSink->Notify(m_pTee, q); } else { // No sink set, so pass it upstream HRESULT hr; IQualityControl * pIQC; hr = VFW_E_NOT_FOUND; if (m_pTee->m_Input.m_Connected) { m_pTee->m_Input.m_Connected->QueryInterface(IID_IQualityControl,(void**)&pIQC); if (pIQC!=NULL) { hr = pIQC->Notify(m_pTee, q); pIQC->Release(); } } return hr; } } // Quality management is too hard to do return NOERROR; } // Notify // // DllRegisterServer // STDAPI DllRegisterServer() { return AMovieDllRegisterServer2( TRUE ); } // // DllUnregisterServer // STDAPI DllUnregisterServer() { return AMovieDllRegisterServer2( FALSE ); }