123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974 |
- //------------------------------------------------------------------------------
- // 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
|