summaryrefslogtreecommitdiff
path: root/third_party/BaseClasses/transip.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/BaseClasses/transip.cpp')
-rw-r--r--third_party/BaseClasses/transip.cpp974
1 files changed, 974 insertions, 0 deletions
diff --git a/third_party/BaseClasses/transip.cpp b/third_party/BaseClasses/transip.cpp
new file mode 100644
index 00000000..e8e12eb5
--- /dev/null
+++ b/third_party/BaseClasses/transip.cpp
@@ -0,0 +1,974 @@
+//------------------------------------------------------------------------------
+// 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