//------------------------------------------------------------------------------ // File: TransIP.cpp // // Desc: DirectShow base classes - implements class for simple Transform- // In-Place filters such as audio. // // Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved. //------------------------------------------------------------------------------ // How allocators are decided. // // An in-place transform tries to do its work in someone else's buffers. // It tries to persuade the filters on either side to use the same allocator // (and for that matter the same media type). In desperation, if the downstream // filter refuses to supply an allocator and the upstream filter offers only // a read-only one then it will provide an allocator. // if the upstream filter insists on a read-only allocator then the transform // filter will (reluctantly) copy the data before transforming it. // // In order to pass an allocator through it needs to remember the one it got // from the first connection to pass it on to the second one. // // It is good if we can avoid insisting on a particular order of connection // (There is a precedent for insisting on the input // being connected first. Insisting on the output being connected first is // not allowed. That would break RenderFile.) // // The base pin classes (CBaseOutputPin and CBaseInputPin) both have a // m_pAllocator member which is used in places like // CBaseOutputPin::GetDeliveryBuffer and CBaseInputPin::Inactive. // To avoid lots of extra overriding, we should keep these happy // by using these pointers. // // When each pin is connected, it will set the corresponding m_pAllocator // and will have a single ref-count on that allocator. // // Refcounts are acquired by GetAllocator calls which return AddReffed // allocators and are released in one of: // CBaseInputPin::Disconnect // CBaseOutputPin::BreakConect // In each case m_pAllocator is set to NULL after the release, so this // is the last chance to ever release it. If there should ever be // multiple refcounts associated with the same pointer, this had better // be cleared up before that happens. To avoid such problems, we'll // stick with one per pointer. // RECONNECTING and STATE CHANGES // // Each pin could be disconnected, connected with a read-only allocator, // connected with an upstream read/write allocator, connected with an // allocator from downstream or connected with its own allocator. // Five states for each pin gives a data space of 25 states. // // Notation: // // R/W == read/write // R-O == read-only // // // // 00 means an unconnected pin. // <- means using a R/W allocator from the upstream filter // <= means using a R-O allocator from an upstream filter // || means using our own (R/W) allocator. // -> means using a R/W allocator from a downstream filter // (a R-O allocator from downstream is nonsense, it can't ever work). // // // That makes 25 possible states. Some states are nonsense (two different // allocators from the same place). These are just an artifact of the notation. // <= <- Nonsense. // <- <= Nonsense // Some states are illegal (the output pin never accepts a R-O allocator): // 00 <= !! Error !! // <= <= !! Error !! // || <= !! Error !! // -> <= !! Error !! // Three states appears to be inaccessible: // -> || Inaccessible // || -> Inaccessible // || <- Inaccessible // Some states only ever occur as intermediates with a pending reconnect which // is guaranteed to finish in another state. // -> 00 ?? unstable goes to || 00 // 00 <- ?? unstable goes to 00 || // -> <- ?? unstable goes to -> -> // <- || ?? unstable goes to <- <- // <- -> ?? unstable goes to <- <- // And that leaves 11 possible resting states: // 1 00 00 Nothing connected. // 2 <- 00 Input pin connected. // 3 <= 00 Input pin connected using R-O allocator. // 4 || 00 Needs several state changes to get here. // 5 00 || Output pin connected using our allocator // 6 00 -> Downstream only connected // 7 || || Undesirable but can be forced upon us. // 8 <= || Copy forced. <= -> is preferable // 9 <= -> OK - forced to copy. // 10 <- <- Transform in place (ideal) // 11 -> -> Transform in place (ideal) // // The object of the exercise is to ensure that we finish up in states // 10 or 11 whenever possible. State 10 is only possible if the upstream // filter has a R/W allocator (the AVI splitter notoriously // doesn't) and state 11 is only possible if the downstream filter does // offer an allocator. // // The transition table (entries marked * go via a reconnect) // // There are 8 possible transitions: // A: Connect upstream to filter with R-O allocator that insists on using it. // B: Connect upstream to filter with R-O allocator but chooses not to use it. // C: Connect upstream to filter with R/W allocator and insists on using it. // D: Connect upstream to filter with R/W allocator but chooses not to use it. // E: Connect downstream to a filter that offers an allocator // F: Connect downstream to a filter that does not offer an allocator // G: disconnect upstream // H: Disconnect downstream // // A B C D E F G H // --------------------------------------------------------- // 00 00 1 | 3 3 2 2 6 5 . . |1 00 00 // <- 00 2 | . . . . *10/11 10 1 . |2 <- 00 // <= 00 3 | . . . . *9/11 *7/8 1 . |3 <= 00 // || 00 4 | . . . . *8 *7 1 . |4 || 00 // 00 || 5 | 8 7 *10 7 . . . 1 |5 00 || // 00 -> 6 | 9 11 *10 11 . . . 1 |6 00 -> // || || 7 | . . . . . . 5 4 |7 || || // <= || 8 | . . . . . . 5 3 |8 <= || // <= -> 9 | . . . . . . 6 3 |9 <= -> // <- <- 10| . . . . . . *5/6 2 |10 <- <- // -> -> 11| . . . . . . 6 *2/3 |11 -> -> // --------------------------------------------------------- // A B C D E F G H // // All these states are accessible without requiring any filter to // change its behaviour but not all transitions are accessible, for // instance a transition from state 4 to anywhere other than // state 8 requires that the upstream filter first offer a R-O allocator // and then changes its mind and offer R/W. This is NOT allowable - it // leads to things like the output pin getting a R/W allocator from // upstream and then the input pin being told it can only have a R-O one. // Note that you CAN change (say) the upstream filter for a different one, but // only as a disconnect / connect, not as a Reconnect. (Exercise for // the reader is to see how you get into state 4). // // The reconnection stuff goes as follows (some of the cases shown here as // "no reconnect" may get one to finalise media type - an old story). // If there is a reconnect where it says "no reconnect" here then the // reconnection must not change the allocator choice. // // state 2: <- 00 transition E <- <- case C <- <- (no change) // case D -> <- and then to -> -> // // state 2: <- 00 transition F <- <- (no reconnect) // // state 3: <= 00 transition E <= -> case A <= -> (no change) // case B -> -> // transition F <= || case A <= || (no change) // case B || || // // state 4: || 00 transition E || || case B -> || and then all cases to -> -> // F || || case B || || (no change) // // state 5: 00 || transition A <= || (no reconnect) // B || || (no reconnect) // C <- || all cases <- <- // D || || (unfortunate, but upstream's choice) // // state 6: 00 -> transition A <= -> (no reconnect) // B -> -> (no reconnect) // C <- -> all cases <- <- // D -> -> (no reconnect) // // state 10:<- <- transition G 00 <- case E 00 -> // case F 00 || // // state 11:-> -> transition H -> 00 case A <= 00 (schizo) // case B <= 00 // case C <- 00 (schizo) // case D <- 00 // // The Rules: // To sort out media types: // The input is reconnected // if the input pin is connected and the output pin connects // The output is reconnected // If the output pin is connected // and the input pin connects to a different media type // // To sort out allocators: // The input is reconnected // if the output disconnects and the input was using a downstream allocator // The output pin calls SetAllocator to pass on a new allocator // if the output is connected and // if the input disconnects and the output was using an upstream allocator // if the input acquires an allocator different from the output one // and that new allocator is not R-O // // Data is copied (i.e. call getbuffer and copy the data before transforming it) // if the two allocators are different. // CHAINS of filters: // // We sit between two filters (call them A and Z). We should finish up // with the same allocator on both of our pins and that should be the // same one that A and Z would have agreed on if we hadn't been in the // way. Furthermore, it should not matter how many in-place transforms // are in the way. Let B, C, D... be in-place transforms ("us"). // Here's how it goes: // // 1. // A connects to B. They agree on A's allocator. // A-a->B // // 2. // B connects to C. Same story. There is no point in a reconnect, but // B will request an input reconnect anyway. // A-a->B-a->C // // 3. // C connects to Z. // C insists on using A's allocator, but compromises by requesting a reconnect. // of C's input. // A-a->B-?->C-a->Z // // We now have pending reconnects on both A--->B and B--->C // // 4. // The A--->B link is reconnected. // A asks B for an allocator. B sees that it has a downstream connection so // asks its downstream input pin i.e. C's input pin for an allocator. C sees // that it too has a downstream connection so asks Z for an allocator. // // Even though Z's input pin is connected, it is being asked for an allocator. // It could refuse, in which case the chain is done and will use A's allocator // Alternatively, Z may supply one. A chooses either Z's or A's own one. // B's input pin gets NotifyAllocator called to tell it the decision and it // propagates this downstream by calling ReceiveAllocator on its output pin // which calls NotifyAllocator on the next input pin downstream etc. // If the choice is Z then it goes: // A-z->B-a->C-a->Z // A-z->B-z->C-a->Z // A-z->B-z->C-z->Z // // And that's IT!! Any further (essentially spurious) reconnects peter out // with no change in the chain. #include #include #include // ================================================================= // Implements the CTransInPlaceFilter class // ================================================================= CTransInPlaceFilter::CTransInPlaceFilter ( TCHAR *pName, LPUNKNOWN pUnk, REFCLSID clsid, HRESULT *phr, bool bModifiesData ) : CTransformFilter(pName, pUnk, clsid), m_bModifiesData(bModifiesData) { #ifdef PERF RegisterPerfId(); #endif // PERF } // constructor #ifdef UNICODE CTransInPlaceFilter::CTransInPlaceFilter ( CHAR *pName, LPUNKNOWN pUnk, REFCLSID clsid, HRESULT *phr, bool bModifiesData ) : CTransformFilter(pName, pUnk, clsid), m_bModifiesData(bModifiesData) { #ifdef PERF RegisterPerfId(); #endif // PERF } // constructor #endif // return a non-addrefed CBasePin * for the user to addref if he holds onto it // for longer than his pointer to us. We create the pins dynamically when they // are asked for rather than in the constructor. This is because we want to // give the derived class an oppportunity to return different pin objects // As soon as any pin is needed we create both (this is different from the // usual transform filter) because enumerators, allocators etc are passed // through from one pin to another and it becomes very painful if the other // pin isn't there. If we fail to create either pin we ensure we fail both. CBasePin * CTransInPlaceFilter::GetPin(int n) { HRESULT hr = S_OK; // Create an input pin if not already done if (m_pInput == NULL) { m_pInput = new CTransInPlaceInputPin( NAME("TransInPlace input pin") , this // Owner filter , &hr // Result code , L"Input" // Pin name ); // Constructor for CTransInPlaceInputPin can't fail ASSERT(SUCCEEDED(hr)); } // Create an output pin if not already done if (m_pInput!=NULL && m_pOutput == NULL) { m_pOutput = new CTransInPlaceOutputPin( NAME("TransInPlace output pin") , this // Owner filter , &hr // Result code , L"Output" // Pin name ); // a failed return code should delete the object ASSERT(SUCCEEDED(hr)); if (m_pOutput == NULL) { delete m_pInput; m_pInput = NULL; } } // Return the appropriate pin ASSERT (n>=0 && n<=1); if (n == 0) { return m_pInput; } else if (n==1) { return m_pOutput; } else { return NULL; } } // GetPin // dir is the direction of our pin. // pReceivePin is the pin we are connecting to. HRESULT CTransInPlaceFilter::CompleteConnect(PIN_DIRECTION dir,IPin *pReceivePin) { UNREFERENCED_PARAMETER(pReceivePin); ASSERT(m_pInput); ASSERT(m_pOutput); // if we are not part of a graph, then don't indirect the pointer // this probably prevents use of the filter without a filtergraph if (!m_pGraph) { return VFW_E_NOT_IN_GRAPH; } // Always reconnect the input to account for buffering changes // // Because we don't get to suggest a type on ReceiveConnection // we need another way of making sure the right type gets used. // // One way would be to have our EnumMediaTypes return our output // connection type first but more deterministic and simple is to // call ReconnectEx passing the type we want to reconnect with // via the base class ReconeectPin method. if (dir == PINDIR_OUTPUT) { if( m_pInput->IsConnected() ) { return ReconnectPin( m_pInput, &m_pOutput->CurrentMediaType() ); } return NOERROR; } ASSERT(dir == PINDIR_INPUT); // Reconnect output if necessary if( m_pOutput->IsConnected() ) { if ( m_pInput->CurrentMediaType() != m_pOutput->CurrentMediaType() ) { return ReconnectPin( m_pOutput, &m_pInput->CurrentMediaType() ); } } return NOERROR; } // ComnpleteConnect // // DecideBufferSize // // Tell the output pin's allocator what size buffers we require. // *pAlloc will be the allocator our output pin is using. // HRESULT CTransInPlaceFilter::DecideBufferSize ( IMemAllocator *pAlloc , ALLOCATOR_PROPERTIES *pProperties ) { ALLOCATOR_PROPERTIES Request, Actual; HRESULT hr; // If we are connected upstream, get his views if (m_pInput->IsConnected()) { // Get the input pin allocator, and get its size and count. // we don't care about his alignment and prefix. hr = InputPin()->PeekAllocator()->GetProperties(&Request); if (FAILED(hr)) { // Input connected but with a secretive allocator - enough! return hr; } } else { // We're reduced to blind guessing. Let's guess one byte and if // this isn't enough then when the other pin does get connected // we can revise it. ZeroMemory(&Request, sizeof(Request)); Request.cBuffers = 1; Request.cbBuffer = 1; } DbgLog((LOG_MEMORY,1,TEXT("Setting Allocator Requirements"))); DbgLog((LOG_MEMORY,1,TEXT("Count %d, Size %d"), Request.cBuffers, Request.cbBuffer)); // Pass the allocator requirements to our output side // but do a little sanity checking first or we'll just hit // asserts in the allocator. pProperties->cBuffers = Request.cBuffers; pProperties->cbBuffer = Request.cbBuffer; if (pProperties->cBuffers<=0) {pProperties->cBuffers = 1; } if (pProperties->cbBuffer<=0) {pProperties->cbBuffer = 1; } hr = pAlloc->SetProperties(pProperties, &Actual); if (FAILED(hr)) { return hr; } DbgLog((LOG_MEMORY,1,TEXT("Obtained Allocator Requirements"))); DbgLog((LOG_MEMORY,1,TEXT("Count %d, Size %d, Alignment %d"), Actual.cBuffers, Actual.cbBuffer, Actual.cbAlign)); // Make sure we got the right alignment and at least the minimum required if ( (Request.cBuffers > Actual.cBuffers) || (Request.cbBuffer > Actual.cbBuffer) || (Request.cbAlign > Actual.cbAlign) ) { return E_FAIL; } return NOERROR; } // DecideBufferSize // // Copy // // return a pointer to an identical copy of pSample IMediaSample * CTransInPlaceFilter::Copy(IMediaSample *pSource) { IMediaSample * pDest; HRESULT hr; REFERENCE_TIME tStart, tStop; const BOOL bTime = S_OK == pSource->GetTime( &tStart, &tStop); // this may block for an indeterminate amount of time hr = OutputPin()->PeekAllocator()->GetBuffer( &pDest , bTime ? &tStart : NULL , bTime ? &tStop : NULL , m_bSampleSkipped ? AM_GBF_PREVFRAMESKIPPED : 0 ); if (FAILED(hr)) { return NULL; } ASSERT(pDest); IMediaSample2 *pSample2; if (SUCCEEDED(pDest->QueryInterface(IID_IMediaSample2, (void **)&pSample2))) { HRESULT hr = pSample2->SetProperties( FIELD_OFFSET(AM_SAMPLE2_PROPERTIES, pbBuffer), (PBYTE)m_pInput->SampleProps()); pSample2->Release(); if (FAILED(hr)) { pDest->Release(); return NULL; } } else { if (bTime) { pDest->SetTime(&tStart, &tStop); } if (S_OK == pSource->IsSyncPoint()) { pDest->SetSyncPoint(TRUE); } if (S_OK == pSource->IsDiscontinuity() || m_bSampleSkipped) { pDest->SetDiscontinuity(TRUE); } if (S_OK == pSource->IsPreroll()) { pDest->SetPreroll(TRUE); } // Copy the media type AM_MEDIA_TYPE *pMediaType; if (S_OK == pSource->GetMediaType(&pMediaType)) { pDest->SetMediaType(pMediaType); DeleteMediaType( pMediaType ); } } m_bSampleSkipped = FALSE; // Copy the sample media times REFERENCE_TIME TimeStart, TimeEnd; if (pSource->GetMediaTime(&TimeStart,&TimeEnd) == NOERROR) { pDest->SetMediaTime(&TimeStart,&TimeEnd); } // Copy the actual data length and the actual data. { const long lDataLength = pSource->GetActualDataLength(); pDest->SetActualDataLength(lDataLength); // Copy the sample data { BYTE *pSourceBuffer, *pDestBuffer; long lSourceSize = pSource->GetSize(); long lDestSize = pDest->GetSize(); ASSERT(lDestSize >= lSourceSize && lDestSize >= lDataLength); pSource->GetPointer(&pSourceBuffer); pDest->GetPointer(&pDestBuffer); ASSERT(lDestSize == 0 || pSourceBuffer != NULL && pDestBuffer != NULL); CopyMemory( (PVOID) pDestBuffer, (PVOID) pSourceBuffer, lDataLength ); } } return pDest; } // Copy // override this to customize the transform process HRESULT CTransInPlaceFilter::Receive(IMediaSample *pSample) { /* Check for other streams and pass them on */ AM_SAMPLE2_PROPERTIES * const pProps = m_pInput->SampleProps(); if (pProps->dwStreamId != AM_STREAM_MEDIA) { return m_pOutput->Deliver(pSample); } HRESULT hr; // Start timing the TransInPlace (if PERF is defined) MSR_START(m_idTransInPlace); if (UsingDifferentAllocators()) { // We have to copy the data. pSample = Copy(pSample); if (pSample==NULL) { MSR_STOP(m_idTransInPlace); return E_UNEXPECTED; } } // have the derived class transform the data hr = Transform(pSample); // Stop the clock and log it (if PERF is defined) MSR_STOP(m_idTransInPlace); if (FAILED(hr)) { DbgLog((LOG_TRACE, 1, TEXT("Error from TransInPlace"))); if (UsingDifferentAllocators()) { pSample->Release(); } return hr; } // the Transform() function can return S_FALSE to indicate that the // sample should not be delivered; we only deliver the sample if it's // really S_OK (same as NOERROR, of course.) if (hr == NOERROR) { hr = m_pOutput->Deliver(pSample); } else { // But it would be an error to return this private workaround // to the caller ... if (S_FALSE == hr) { // S_FALSE returned from Transform is a PRIVATE agreement // We should return NOERROR from Receive() in this cause because // returning S_FALSE from Receive() means that this is the end // of the stream and no more data should be sent. m_bSampleSkipped = TRUE; if (!m_bQualityChanged) { NotifyEvent(EC_QUALITY_CHANGE,0,0); m_bQualityChanged = TRUE; } hr = NOERROR; } } // release the output buffer. If the connected pin still needs it, // it will have addrefed it itself. if (UsingDifferentAllocators()) { pSample->Release(); } return hr; } // Receive // ================================================================= // Implements the CTransInPlaceInputPin class // ================================================================= // constructor CTransInPlaceInputPin::CTransInPlaceInputPin ( TCHAR *pObjectName , CTransInPlaceFilter *pFilter , HRESULT *phr , LPCWSTR pName ) : CTransformInputPin(pObjectName, pFilter, phr, pName) , m_bReadOnly(FALSE) , m_pTIPFilter(pFilter) { DbgLog((LOG_TRACE, 2 , TEXT("CTransInPlaceInputPin::CTransInPlaceInputPin"))); } // constructor // ================================================================= // Implements IMemInputPin interface // ================================================================= // If the downstream filter has one then offer that (even if our own output // pin is not using it yet. If the upstream filter chooses it then we will // tell our output pin to ReceiveAllocator). // Else if our output pin is using an allocator then offer that. // ( This could mean offering the upstream filter his own allocator, // it could mean offerring our own // ) or it could mean offering the one from downstream // Else fail to offer any allocator at all. STDMETHODIMP CTransInPlaceInputPin::GetAllocator(IMemAllocator ** ppAllocator) { CheckPointer(ppAllocator,E_POINTER); ValidateReadWritePtr(ppAllocator,sizeof(IMemAllocator *)); CAutoLock cObjectLock(m_pLock); HRESULT hr; if ( m_pTIPFilter->m_pOutput->IsConnected() ) { // Store the allocator we got hr = m_pTIPFilter->OutputPin()->ConnectedIMemInputPin() ->GetAllocator( ppAllocator ); if (SUCCEEDED(hr)) { m_pTIPFilter->OutputPin()->SetAllocator( *ppAllocator ); } } else { // Help upstream filter (eg TIP filter which is having to do a copy) // by providing a temp allocator here - we'll never use // this allocator because when our output is connected we'll // reconnect this pin hr = CTransformInputPin::GetAllocator( ppAllocator ); } return hr; } // GetAllocator /* Get told which allocator the upstream output pin is actually going to use */ STDMETHODIMP CTransInPlaceInputPin::NotifyAllocator( IMemAllocator * pAllocator, BOOL bReadOnly) { HRESULT hr = S_OK; CheckPointer(pAllocator,E_POINTER); ValidateReadPtr(pAllocator,sizeof(IMemAllocator)); CAutoLock cObjectLock(m_pLock); m_bReadOnly = bReadOnly; // If we modify data then don't accept the allocator if it's // the same as the output pin's allocator // If our output is not connected just accept the allocator // We're never going to use this allocator because when our // output pin is connected we'll reconnect this pin if (!m_pTIPFilter->OutputPin()->IsConnected()) { return CTransformInputPin::NotifyAllocator(pAllocator, bReadOnly); } // If the allocator is read-only and we're modifying data // and the allocator is the same as the output pin's // then reject if (bReadOnly && m_pTIPFilter->m_bModifiesData) { IMemAllocator *pOutputAllocator = m_pTIPFilter->OutputPin()->PeekAllocator(); // Make sure we have an output allocator if (pOutputAllocator == NULL) { hr = m_pTIPFilter->OutputPin()->ConnectedIMemInputPin()-> GetAllocator(&pOutputAllocator); if(FAILED(hr)) { hr = CreateMemoryAllocator(&pOutputAllocator); } if (SUCCEEDED(hr)) { m_pTIPFilter->OutputPin()->SetAllocator(pOutputAllocator); pOutputAllocator->Release(); } } if (pAllocator == pOutputAllocator) { hr = E_FAIL; } else if(SUCCEEDED(hr)) { // Must copy so set the allocator properties on the output ALLOCATOR_PROPERTIES Props, Actual; hr = pAllocator->GetProperties(&Props); if (SUCCEEDED(hr)) { hr = pOutputAllocator->SetProperties(&Props, &Actual); } if (SUCCEEDED(hr)) { if ( (Props.cBuffers > Actual.cBuffers) || (Props.cbBuffer > Actual.cbBuffer) || (Props.cbAlign > Actual.cbAlign) ) { hr = E_FAIL; } } // Set the allocator on the output pin if (SUCCEEDED(hr)) { hr = m_pTIPFilter->OutputPin()->ConnectedIMemInputPin() ->NotifyAllocator( pOutputAllocator, FALSE ); } } } else { hr = m_pTIPFilter->OutputPin()->ConnectedIMemInputPin() ->NotifyAllocator( pAllocator, bReadOnly ); if (SUCCEEDED(hr)) { m_pTIPFilter->OutputPin()->SetAllocator( pAllocator ); } } if (SUCCEEDED(hr)) { // It's possible that the old and the new are the same thing. // AddRef before release ensures that we don't unload it. pAllocator->AddRef(); if( m_pAllocator != NULL ) m_pAllocator->Release(); m_pAllocator = pAllocator; // We have an allocator for the input pin } return hr; } // NotifyAllocator // EnumMediaTypes // - pass through to our downstream filter STDMETHODIMP CTransInPlaceInputPin::EnumMediaTypes( IEnumMediaTypes **ppEnum ) { // Can only pass through if connected if( !m_pTIPFilter->m_pOutput->IsConnected() ) return VFW_E_NOT_CONNECTED; return m_pTIPFilter->m_pOutput->GetConnected()->EnumMediaTypes( ppEnum ); } // EnumMediaTypes // CheckMediaType // - agree to anything if not connected, // otherwise pass through to the downstream filter. // This assumes that the filter does not change the media type. HRESULT CTransInPlaceInputPin::CheckMediaType(const CMediaType *pmt ) { HRESULT hr = m_pTIPFilter->CheckInputType(pmt); if (hr!=S_OK) return hr; if( m_pTIPFilter->m_pOutput->IsConnected() ) return m_pTIPFilter->m_pOutput->GetConnected()->QueryAccept( pmt ); else return S_OK; } // CheckMediaType // If upstream asks us what our requirements are, we will try to ask downstream // if that doesn't work, we'll just take the defaults. STDMETHODIMP CTransInPlaceInputPin::GetAllocatorRequirements(ALLOCATOR_PROPERTIES *pProps) { if( m_pTIPFilter->m_pOutput->IsConnected() ) return m_pTIPFilter->OutputPin() ->ConnectedIMemInputPin()->GetAllocatorRequirements( pProps ); else return E_NOTIMPL; } // GetAllocatorRequirements // CTransInPlaceInputPin::CompleteConnect() calls CBaseInputPin::CompleteConnect() // and then calls CTransInPlaceFilter::CompleteConnect(). It does this because // CTransInPlaceFilter::CompleteConnect() can reconnect a pin and we do not // want to reconnect a pin if CBaseInputPin::CompleteConnect() fails. HRESULT CTransInPlaceInputPin::CompleteConnect(IPin *pReceivePin) { HRESULT hr = CBaseInputPin::CompleteConnect(pReceivePin); if (FAILED(hr)) { return hr; } return m_pTransformFilter->CompleteConnect(PINDIR_INPUT,pReceivePin); } // CompleteConnect // ================================================================= // Implements the CTransInPlaceOutputPin class // ================================================================= // constructor CTransInPlaceOutputPin::CTransInPlaceOutputPin( TCHAR *pObjectName, CTransInPlaceFilter *pFilter, HRESULT * phr, LPCWSTR pPinName) : CTransformOutputPin( pObjectName , pFilter , phr , pPinName), m_pTIPFilter(pFilter) { DbgLog(( LOG_TRACE, 2 , TEXT("CTransInPlaceOutputPin::CTransInPlaceOutputPin"))); } // constructor // EnumMediaTypes // - pass through to our upstream filter STDMETHODIMP CTransInPlaceOutputPin::EnumMediaTypes( IEnumMediaTypes **ppEnum ) { // Can only pass through if connected. if( ! m_pTIPFilter->m_pInput->IsConnected() ) return VFW_E_NOT_CONNECTED; return m_pTIPFilter->m_pInput->GetConnected()->EnumMediaTypes( ppEnum ); } // EnumMediaTypes // CheckMediaType // - agree to anything if not connected, // otherwise pass through to the upstream filter. HRESULT CTransInPlaceOutputPin::CheckMediaType(const CMediaType *pmt ) { // Don't accept any output pin type changes if we're copying // between allocators - it's too late to change the input // allocator size. if (m_pTIPFilter->UsingDifferentAllocators() && !m_pFilter->IsStopped()) { if (*pmt == m_mt) { return S_OK; } else { return VFW_E_TYPE_NOT_ACCEPTED; } } // Assumes the type does not change. That's why we're calling // CheckINPUTType here on the OUTPUT pin. HRESULT hr = m_pTIPFilter->CheckInputType(pmt); if (hr!=S_OK) return hr; if( m_pTIPFilter->m_pInput->IsConnected() ) return m_pTIPFilter->m_pInput->GetConnected()->QueryAccept( pmt ); else return S_OK; } // CheckMediaType /* Save the allocator pointer in the output pin */ void CTransInPlaceOutputPin::SetAllocator(IMemAllocator * pAllocator) { pAllocator->AddRef(); if (m_pAllocator) { m_pAllocator->Release(); } m_pAllocator = pAllocator; } // SetAllocator // CTransInPlaceOutputPin::CompleteConnect() calls CBaseOutputPin::CompleteConnect() // and then calls CTransInPlaceFilter::CompleteConnect(). It does this because // CTransInPlaceFilter::CompleteConnect() can reconnect a pin and we do not want to // reconnect a pin if CBaseOutputPin::CompleteConnect() fails. // CBaseOutputPin::CompleteConnect() often fails when our output pin is being connected // to the Video Mixing Renderer. HRESULT CTransInPlaceOutputPin::CompleteConnect(IPin *pReceivePin) { HRESULT hr = CBaseOutputPin::CompleteConnect(pReceivePin); if (FAILED(hr)) { return hr; } return m_pTransformFilter->CompleteConnect(PINDIR_OUTPUT,pReceivePin); } // CompleteConnect