forked from LeenkxTeam/LNXSDK
Update Files
This commit is contained in:
@ -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
|
Reference in New Issue
Block a user