DirectShow Learning (7): CTRANSINPLACEFILTER and the source code resolution of the associated PIN class
1. CTRANSINPLACEINPUTPIN Class [Transip.h / Transip.cpp] derived from CTRANSFORMINPUTPIN.
a) Member variable: CTransinplaceFilter * const m_ptipfilter; // outfilter m_breadonly; // incoming stream is read Onlym_BreadOnly initialized to False.
b) IMemInputPin interface functions // Return our upstream allocator // 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 GetAllocator (IMemAllocator ** ppAllocator); {CAutoLock cObjectLock (m_pLock); 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 {hr = CTransformInputPin :: GetAllocator (ppallocator);}} // get told which allocator the upstream output pin is actually going to use.STDMETHODIMP NotifyAllocator (IMemAllocator * pAllocator, BOOL bReadOnly); {CAutoLock cObjectLock (m_pLock); m_bReadOnly = bReadOnly; // If we modify data then do not 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_ptfilter-> 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_ptifilter-> Outputpin () -> setallocator; poutputallocator-> release ();}} if (pallocator == poutputallocator) {hr = e_fail;} else if (succeeded (hr)) {// Must Copy Set The Allocator Properties on The Output Allocator_Properties PROPS, ACTUAL; HR = Pallocator-> getProperties (& props); if (succeededed (hr)) { HR = poutputallocator-> setProperties (& props, & actual);} f (successded (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 do not unload it pAllocator-> AddRef ();.. if (! m_pAllocator = NULL) m_pAllocator-> Release (); m_pAllocator = pAllocator; // We have an allocator for the input pin}} // Pass this on downstream if it ever gets called.STDMETHODIMP GetAllocatorRequirements (ALLOCATOR_PROPERTIES * pProps); {if (m_pTIPFilter-> m_pOutput-> IsConnected ()) return m_pTIPFilter- > OUTPUTPIN () -> ConnectedimeMinputpin () -> getAllocatorRequirements (PPROPS); Else Return E_NOTIMPL;
c) IPin interface function and CBasePin, CBaseInputPin inherit function // Provide an enumerator for media types by getting one from downstreamSTDMETHODIMP EnumMediaTypes (IEnumMediaTypes ** ppEnum); {// Can only pass through if connected if (m_pTIPFilter-> m_pOutput->! IsConnected ()) return VFW_E_NOT_CONNECTED; return m_pTIPFilter-> m_pOutput-> getConnected () -> EnumMediaTypes (ppEnum);.} // Say whether media type is acceptable - 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 CheckMediaType (const CMediaType * pmt); {HRESULT hr = m_pTIPFilter-> CheckInputType (pmt); if (m_pTIPFilter-> m_pOutput-> IsConnected ()) return m_pTIPFilter-> m_pOutput-> getConnected () -> QueryAccept (pmt); else return S_OK;} HRESULT CompleteConnect (IPin * pReceivePin); {HRESULT hr = CBaseInputPin :: CompleteConnect (pReceivePin); return m_pTransformFilter-> CompleteConnect (PINDIR_INPUT, pReceivePin ); d) Other functions // allow the filter to see what allocator we have name n.b. this does not addrefime mallocator * peekallocator () const {return m_pallocator;} inline const bool readonly ()}}
2. CTransinplaceoutputPin class [transip.h / transip.cpp]
a) Member variables CTransinplaceFilter * const m_ptipfilter;
b) IPin interface function, CBaseOutputPin functions // Provide a media type enumerator Get it from upstream.STDMETHODIMP EnumMediaTypes. (IEnumMediaTypes ** ppEnum);! {if (m_pTIPFilter-> m_pInput-> IsConnected ()) return VFW_E_NOT_CONNECTED; return m_pTIPFilter- > m_pInput-> getConnected () -> EnumMediaTypes (ppEnum);} // Say whether media type is acceptable - agree to anything if not connected, // otherwise pass through to the upstream filter.HRESULT CheckMediaType (const CMediaType * pmt). ; {if (m_pTIPFilter-> UsingDifferentAllocators () && m_pFilter-> isStopped ()!) {if (* pmt == m_mt) {return S_OK;} else {return VFW_E_TYPE_NOT_ACCEPTED;}} HRESULT hr = m_pTIPFilter-> CheckInputType (pmt) ; if (m_pTIPFilter-> m_pInput-> IsConnected ()) return m_pTIPFilter-> m_pInput-> getConnected () -> QueryAccept (pmt); else return S_OK;} HRESULT CompleteConnect (IPin * pReceivePin); {HRESULT hr = CBaseOutputPin :: CompleteConnect (PreceivePin); Return M_PTRANSFORMFILTER-> CompleteConnec t (PINDIR_OUTPUT, pReceivePin);} c) Other functions // This just saves the allocator being used on the output pin // Also called by input pin's GetAllocator () void SetAllocator (IMemAllocator * pAllocator); {pAllocator-> AddRef () ; if (m_pAllocator) {m_pAllocator-> Release ();} m_pAllocator = pAllocator;} IMemInputPin * ConnectedIMemInputPin () {return m_pInputPin;} IMemAllocator * PeekAllocator () const {return m_pAllocator;}
3. CTransinplaceFilter class [transip.h / transip.cpp]
a) Member variable BOOL M_BMODIFIFIESDATA; / / DOES THIS FILTER CHANGE THE DATA? M_BMODIFIESDATA is initialized by BModiFiesData in the Constructor.
b) Newly added Virtual function Virtual HRESULT TRANSFORM (IMEDIASAMPLE * PSAMPLE) PURE
c) inherited IBaseFilter, CBaseFilter and CTransformFilter functions // chance to customize the transform processvirtual HRESULT 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);} if (UsingDifferentAllocators ()) {pSample = Copy (pSample);} // have the derived class transform the data hr = Transform ( pSample); hr = m_pOutput-> Deliver (pSample); if (UsingDifferentAllocators ()) {pSample-> Release ();}} HRESULT CompleteConnect (PIN_DIRECTION dir, IPin * pReceivePin); {if (dir == PINDIR_OUTPUT) {if (m_pInput-> IsConnected ()) {return ReconnectPin (m_pInput, & m_pOutput-> CurrentMediaType ());} return NOERROR;!} if (m_pOutput-> IsConnected ()) {if (m_pInput-> CurrentMediaType () = m_pOutput-> CurrentMediatype ()) {Return ReconnectPi n (m_pOutput, & m_pInput-> CurrentMediaType ());}}} // must overrideHRESULT GetMediaType (int iPosition, CMediaType * pMediaType) // This is called when we actually have to provide out own allocator.HRESULT DecideBufferSize (IMemAllocator *, ALLOCATOR_PROPERTIES *); {// if we are connect upstream, get his views if (m_pinput-> isconnected ()) {hr = inputpin () -> peekallocator () -> getproperties (& recommended);} // Plug Allocator_Properties, and call Palloc-> setProperties (Pproperties, & Actual);
{// 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); if (m_pint! = Null && m_poutput == null) {m_poutput = new cTransinplaceoutputPin (Name ("Transinplace Output Pin"), this // Owner Filter, & HR // Result Code, L "Output" // Pin name); if (n == 0) {return m_pinput;} else == 1) {return m_poutput;}}} d) Other functions iMediasample * cTransinplaceFilter :: copy (iMediasample * psource ); Copy two sample} CTransinplacei nputPin * InputPin () const {return (CTransInPlaceInputPin *) m_pInput;} CTransInPlaceOutputPin * OutputPin () const {return (CTransInPlaceOutputPin *) m_pOutput;} // Helper to see if the input and output types matchBOOL TypesMatch () {return InputPin () -> CurrentMediaType () == OutputPin () -> CurrentMediaType ();} // Are the input and output allocators different BOOL UsingDifferentAllocators () const {return InputPin () -> PeekAllocator () = OutputPin () -> PeekAllocator?! ();
Attached to the design idea annotation in CTransinplacefilter, these classes should be the most comments in Base Classes, the easiest class. // How allocators area 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 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 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 ORDER OF Connection (There Is A Precedent
// for insisting on the input being connection first. insisting on the output being connected. INSISTING INTED
// first is not allowed. That Would Break Renderfile.)
// the base pin classes (CBASEOUTPUTPIN AND CBASEINPUTPIN) BOTH HAVE A M_PALLOCATOR MEMBER Which
// is buy in placepin :: getDeliverybuffer and cbaseinputpin :: inactive.
// TO Avoid Lots of extra overriding, We Should Keep these Happy, 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 is 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 is the Same Pointer, this, THIS, IF THESETE.
// Had Better Be Cleared Up Before That PROBLEMS, We'll Stick with One // Per Pointer.
// Reconnecting and State Changes
// Each PIN COULD BE Disconnected, Connected with a Read-Only Allocator, Connected with an upstream
// read / Write Allocator, Connected with an allocator from DownStream or Connected with ITS OWN
// Allocator. Five States for Each PIN GIVES A DATA Space of 25 States.
// NOTATION:
// r / w == Read / Write
// r-o == read-only
//
//
// 00 means an unconnected pin.
// <- means using a r / w Allocator from the Upstream Filter
// <= means using a r-o allocator from an upstream filter
// || Means Using Our OWN (R / W) Allocator.
// -> Means Using A R / W Allocator from a DownStream Filter
// (A R-O Allocator from DownStream Is Nonsense, IT CAN't Ever Work).
//
// That Makes 25 Possible State. Some States Are Nonsense (Two Different
// Allocators from the Same Place. The Ree 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 State Appears to be inaccessible:
// -> || inaccessible
// || -> inaccessible
// || <- inaccessible
// Some State Only Ever Occur As Intermediate with a Pending Reconnect Which
// is Guaranteed to finish in another.
// -> 00 ?? Unstable Goes to || 00 00
// 00 <- ?? unstable goes to 00 || // -> <- ?? unsteable goes to -> ->
// <- || ?? unsteable goes to <- <-
// <- -> ?? unsteable goes to <- <-
// and That Leaves 11 Possible Resting State:
// 1 00 00 Nothing connect.
// 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 Preference
// 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 State 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 8 Possible Transitions:
// A: Connect Upstream To FILTER WITH R-O Allocator That INSSTS 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 triaes not offer an allocator
// g: Disconnect Upstream
// h: disconnect downstream //
// a b c d e f g
/ / -------------------------------------------------------------------------------------------- ---------
// 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
//
// All these State 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 the 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 the the INPUT PIN Being Told It Can Only Have A // R-O One.
// Note That You Chan 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 Get Into State 4).
//
// The reconnection stuff goes as Follows (some of the case shown here as "no reconnect" May
// Get One to finalise Media Type - An Old Story). if it is a reconnect where it, "no
// reconnect "Here The Reconnection Must Not Change The Allocator Choice.
// state 2: <- 00 Transition E <- <- Case C <- <- (no change)
// Case D -> <- and dam> -> ->
//
// 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 the 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 case <- <- <-
// 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 connection 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 diffrom the Output One
// and this 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 The and Z). We shop 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
// is 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 See 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 Dowstream 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
// Alternative, 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 z1n it ing:
// 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 (essential solious) reconnects peter out with no change in
// the chain.
Previous Article:
(6) Analysis of source code analysis of CTransfromFilter and associated PIN class