| //------------------------------------------------------------------------------ | |
| // 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 | |
| // | |
| // <input pin state> <output pin state> <comments> | |
| // | |
| // 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 <streams.h> | |
| #include <measure.h> | |
| #include <transip.h> | |
| // ================================================================= | |
| // Implements the CTransInPlaceFilter class | |
| // ================================================================= | |
| CTransInPlaceFilter::CTransInPlaceFilter | |
| ( __in_opt LPCTSTR pName, | |
| __inout_opt LPUNKNOWN pUnk, | |
| REFCLSID clsid, | |
| __inout HRESULT *phr, | |
| bool bModifiesData | |
| ) | |
| : CTransformFilter(pName, pUnk, clsid), | |
| m_bModifiesData(bModifiesData) | |
| { | |
| #ifdef PERF | |
| RegisterPerfId(); | |
| #endif // PERF | |
| } // constructor | |
| #ifdef UNICODE | |
| CTransInPlaceFilter::CTransInPlaceFilter | |
| ( __in_opt LPCSTR pName, | |
| __inout_opt LPUNKNOWN pUnk, | |
| REFCLSID clsid, | |
| __inout 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 | |
| , __inout 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 { | |
| // Propose one byte | |
| // 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; | |
| pProperties->cbAlign = Request.cbAlign; | |
| 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 | |
| __out_opt 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 hrProps = pSample2->SetProperties( | |
| FIELD_OFFSET(AM_SAMPLE2_PROPERTIES, pbBuffer), | |
| (PBYTE)m_pInput->SampleProps()); | |
| pSample2->Release(); | |
| if (FAILED(hrProps)) { | |
| 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(); | |
| if (FAILED(pDest->SetActualDataLength(lDataLength))) { | |
| pDest->Release(); | |
| return NULL; | |
| } | |
| // Copy the sample data | |
| { | |
| BYTE *pSourceBuffer, *pDestBuffer; | |
| long lSourceSize = pSource->GetSize(); | |
| long lDestSize = pDest->GetSize(); | |
| ASSERT(lDestSize >= lSourceSize && lDestSize >= lDataLength); | |
| if (FAILED(pSource->GetPointer(&pSourceBuffer)) || | |
| FAILED(pDest->GetPointer(&pDestBuffer)) || | |
| lDestSize < lDataLength || | |
| lDataLength < 0) { | |
| pDest->Release(); | |
| return NULL; | |
| } | |
| 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 | |
| ( __in_opt LPCTSTR pObjectName | |
| , __inout CTransInPlaceFilter *pFilter | |
| , __inout HRESULT *phr | |
| , __in_opt 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(__deref_out 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( __deref_out 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(__out 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( | |
| __in_opt LPCTSTR pObjectName, | |
| __inout CTransInPlaceFilter *pFilter, | |
| __inout HRESULT * phr, | |
| __in_opt 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( __deref_out 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 |