Important:
This is retired content. This content is outdated and is no longer being maintained. It is provided as a courtesy for individuals who are still using these technologies. This content may contain URLs that were valid when originally published, but now link to sites or pages that no longer exist.
A version of this page is also available for
4/8/2010

The CBasePinclass and its derived base classes, CBaseOutputPinand CBaseInputPin, implement most of the mechanism for the most common connection scenarios, much of which can be overridden by the derived filter class for more control of the process.

The connection procedure relies on the implementation of four interfaces:

The IMemInputPinand IMemAllocatorinterfaces are necessary only if the filter belonging to the connecting input pin (called the downstream filter) is expected to provide a shared memory allocator for transporting samples between the pins. However, the base class implementation in CBaseInputPinassumes this condition and implements IMemInputPinto provide an allocator object to a connected output pin that requests it.

In the connection scenario of the default base class, the pin classes derived from CBaseInputPinand CBaseOutputPinneed only to override and implement a few member functions and can let the base classes do the remaining work. Base classes derived from these classes, such as CTransformInputPinand CTransformOutputPin, do much of the required implementation to provide a default connection scheme.

Pin classes derived from CBaseInputPinand CBaseOutputPinneed only to override the following member functions to enable pin connection:

  • CBasePin::CheckMediaType, which is called for every media type proposed by the media type enumerator. The overriding member function must accept or reject the proposed media type.

  • CBasePin::GetMediaType, which is called by the media type of the output pin enumerator to suggest media types already agreed on by the input pin for transform filters. This member function also presents the type of media a source filter will produce.

Additionally, the output pin derived from the CBaseOutputPinclass must override the CBaseOutputPin::DecideBufferSizemember function. This is called by the base classes to let the output pin inform any acquired allocator of the size and type of media samples that the output pin will provide. This is done by the output pin of the filter because the derived filter class should know the type and size of the data it will send to the input pin of the connected filter.

To see the context of these overriding functions, it is helpful to step through the execution of the connection code in the class library. All connection takes place in the scope of one CBasePin::Connectmember function.

This section contains the following topics.

The Filter Graph Manager Starts the Connection

The connection starts when the filter graph manager calls the IPin::Connectmethod on the output pin, passing it a pointer to the input pin to which it is connecting. The filter graph manager has previously retrieved pointers to the IPininterfaces of both filters, for example, by calling the IBaseFilter::EnumPinsmethod on each connecting filter. The EnumPinsmethod creates a CEnumPinsobject to enumerate the pins, which the enumerator does by repeatedly calling the CBaseFilter::GetPinmember function of the derived filter, which the derived filter must implement.

The CBasePin::Connectimplementation of IPin::Connectdoes much of the work in this case. It calls the following functions.

The CBasePin::CheckConnectimplementation simply determines that the pin directions are different. The overriding CBaseOutputPin::CheckConnectmember function calls the IUnknown::QueryInterfacemethod of the connected input pin to retrieve a pointer to the IMemInputPininterface of that pin. This will be used later in the connection process to request an allocator from the connected input pin. (Your derived class can override CBaseOutputPin::CheckConnectand omit retrieving the IMemInputPininterface if the output pin already has an allocator; for example, it might want to use the allocator from an upstream filter to eliminate copying.)

Negotiating Media Types with CBasePin::AgreeMediaType

The CBasePin::AgreeMediaTypemember function is called next and attempts to negotiate a media type that both pins agree on. It does this by trying to find a media type presented by the connected input pin with which the output pin agrees. If that fails, it tries to find a media type preferred by the output pin that the connected input pin agrees with.

CBasePin::AgreeMediaTypecalls the following member functions and methods:

The IPin::EnumMediaTypesmethod of the connected input pin is called to return a media type enumerator ( IEnumMediaTypes). This allows the output pin to examine the list of preferred media types belonging to the input pin.

The IEnumMediaTypes::Nextmethod of the enumerator calls the GetMediaTypemember function of the derived input pin to retrieve each media type. If GetMediaTypeis not implemented, the base class implementation returns an error but this does not necessarily break the connection. (Pins are not required to have a preferred media type if one pin or the other can propose a type that they both accept. If neither pin can propose types, the connection will fail.)

Determining a Media Type with CBasePin::TryMediaTypes

CBasePin::AgreeMediaTypecalls CBasePin::TryMediaTypesnext. The TryMediaTypesmember function cycles through the preferred media types of the connected input pin and calls the CBasePin::CheckMediaTypemember function of the derived output pin class for each one it finds. CheckMediaTypemust be implemented by your derived output pin class. If CheckMediaTypeaccepts the media type, the IPin::ReceiveConnectionmethod of the connected input pin is called with the media type to determine if the connected input pin accepts this media type. If so, TryMediaTypescalls the CBaseOutputPin::CompleteConnectmember function to finish the connection to the input pin.

If the input pin has no media types that the output type can use, CBasePin::AgreeMediaTyperepeats the entire process, using the enumerator for the media types of the output pin. (That is, it gets its own enumerator and calls TryMediaTypeswith each of its preferred media types.) Again, the enumerator calls GetMediaTypefor each media type in the list. In this case, GetMediaTypeshould be implemented to provide a media type. If the filter is a source filter, it will have a definite media type to export. If the filter is a transform filter, the media type will be established between the filter's input pin and its connected pin; the transform filter should query for that media type or simply use the enumerator of the upstream filter (unless the transform filter changes the media type from input pin to output pin).

CheckMediaTypeis called by CBasePin::TryMediaTypes, even when TryMediaTypesenumerates the list of the preferred media types of the output pin. This is because the owning filter might be a transform-in-place filter that is simply using the media type (and enumerator) of an upstream filter; this is the point at which the filter determines if the media type is compatible. The input pin of this transform filter might defer selecting a media type when it is connected, in which case it would be up to the output pin of the transform filter to ensure the media type is compatible with its transform.

If a media type can be established, TryMediaTypeseventually calls the CBaseOutputPin::CompleteConnectmember function to negotiate a memory allocator.

First, the CBaseOutputPin::CompleteConnectmember function calls the CBaseOutputPin::DecideAllocatormember function. This member function negotiates a shared memory allocator with the input pin. It does this by first calling the IMemInputPin::GetAllocatormethod of the connected input pin, which retrieves a pointer to an IMemAllocatorinterface provided by the input pin.

Then, CompleteConnectcalls the pure virtual CBaseOutputPin::DecideBufferSizemember function, which your derived output pin class must override and implement because only the derived class can determine the required buffer size for its media type.

Finally, CompleteConnectcalls the IMemInputPin::NotifyAllocatormethod of the connected pin to inform the input pin of the allocator to use and to provide a pointer to it. The input pin can reject this allocator, in which case the output pin can retry with a different allocator or fail the connection. If your derived class is not using the allocator of the connected input pin, override CBaseOutputPin::DecideAllocatorin your derived class to call the NotifyAllocatormember function with an allocator.

When a Reconnection Should Occur

Reconnection is always performed through the IFilterGraphinterface on the filter graph manager. Reconnect by calling the IFilterGraph2::ReconnectExmethod or the IFilterGraph::Reconnectmethod, both of which pass the IPininterfaces of the two pins to be reconnected. The ReconnectExmethod specifies a media type and thus removes the burden of remembering what type to reconnect with from the pins, which makes the reconnection more likely to succeed.

Filters are typically connected with the upstream filter first and the downstream filter second. Therefore, the filter negotiates the connection on its input pin before information is available about the filter being connected to its output pin. When the output pin of the filter connects, it may become clear that the media type or allocator that was established for the input pin of the filter are not appropriate. In this case, the input connection can be broken and reconnected.

For example, consider the following connection scenario. An audio effects filter (for example, a reverberation effect) is inserted between an MPEG-audio decompressor filter and another audio effects filter. During the upstream connection to the decompressor filter, a media type is chosen - for example, 22.05 kHz, 16-bit mono. However, in this scenario, when the reverberation filter connects its output pin, the downstream filter will accept only an 11.025 kHz, 16-bit mono media type. Therefore, after connecting with the downstream filter, the reverberation effects filter must then reconnect with the upstream filter and negotiate for an 11.025 kHz media type.

But media types are not the only reason for reconnection. In many cases, the filter is a transform-inplace filter; that is, a filter that does not require that it either alters the media type or copies the data. Such a filter can be designed to use an allocator of some other filter (upstream or downstream), and likewise use the media type of another filter. That is, the filter is doing its transform "in place" in the buffer of another filter (for example, in the file buffer of the source filter or the video buffer of the rendering filter).

The general rule is that filters of this type should offer the allocator of the downstream filter to the upstream filter, once the allocator has been established for the output pin. This requires a reconnection of the input pin so that, when the input pin is asked for an allocator (in IMemInputPin::GetAllocator) by the upstream output pin, it can offer the allocator retrieved from the downstream filter by the output pin of the transform filter. Therefore, in-place transforms always reconnect.

There are a couple of important rules to follow when requesting a reconnection.

First, a filter must never request a reconnection unless it is certain that the reconnection will succeed. If the reconnection fails, it causes an asynchronous error in the filter graph for which there is no obvious cleanup. Any error that occurs (for example, from incompatible media types) should occur when the pins are connected the first time, when there are ample retry options available at more than one level (by the filter graph manager or the application at least).

Second, a filter should request a reconnection on the same thread as the call to IPin::Connect. For example, the following scenario attempts reconnection on a separate thread and will cause problems.

  • The filter graph manager calls Connecton a pin.

  • The filter pin carries out the Connectmethod and creates a thread, which starts to determine whether everything is okay for the connection.

  • Connectreturns to the filter graph manager.

  • The filter graph manager returns to the application.

  • The application calls the IMediaControl::Runmethod of the filter graph manager to start the filter graph, and the filters start running.

  • The thread from the initial connection calls the IFilterGraph2::ReconnectExor IFilterGraph::Reconnectmethod and the filter graph manager attempts to carry out reconnection.

  • Failure occurs because the filters cannot reconnect while in the running state.

The filter graph has code to prevent this failure as long as the IFilterGraph2::ReconnectExor IFilterGraph::Reconnectmethod takes effect while the filter graph is still processing the IGraphBuilder::Connectmethod. Calling the filter graph to reconnect before returning from the IPin::Connectmethod is the best way to ensure this problem does not occur. The best way to achieve this is to perform all of this on the same thread.