본문 바로가기

3.구현/VC++

DirectShow base classes - implements class for simple Transform-In-Place filters

------------------------------------------------------------------------------

File: TransIP.cpp

Desc: DirectShow base classes - implements class for simple Transform-

      In-Place filters such as audio.

Copyright (c) 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 BaseInputPin::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.

반응형