Microsoft DirectX 8.0

Data Flow in the Filter Graph

This article describes how data moves through a Microsoft® DirectShow® filter graph. It contains the following topics.

Samples and Buffers

When two pins are connected, data can move from the output pin to the input pin. The output pin delivers data, while the input pin receives data. The direction of data flow, from output to input, is called downstream, and the opposite direction is called upstream.

The type of data that moves between two pins depends on the implementation of the pins. In the most common case, pins work with media data that is held in main memory. However, other arrangements are possible. For example, if two filters control a piece of video hardware, the hardware might handle all of the video data, with the pins exchanging control information. The type of data, and how it moves between pins, is called the transport. This article focuses on the case where media data is held in main memory, called the local memory transport.

In the local memory transport, data is packaged into discrete objects called media samples. A media sample is a COM object that holds a pointer to an allocated memory buffer. Media samples support the IMediaSample interface, and possibly the IMediaSample2 interface.

Another COM object, the memory allocator, is responsible for allocating buffers and creating media samples. At connection time, the allocator reserves memory for the buffers. The allocator also creates a set of media samples, and gives each sample a pointer to an address within the memory block. Until the buffers are freed, the allocator maintains a list of which samples are available. Whenever a filter needs a new sample, it requests one from the allocator. After a sample is processed, the sample returns to the list.

This mechanism reduces the amount of memory allocation, because filters re-use the same buffers. It also prevents filters from accidentally writing over data that has not been processed, because the allocator maintains the list of available samples. Finally, it provides an efficient way to move data through the graph: When an output pin delivers a sample, it passes a pointer to the sample's IMediaSample interface. It does not have to copy any data.

The following illustration shows the relationship between the allocator, the media samples, and the filter.

Connected pins share a memory allocator. A filter might use different allocators for its input and output. This is typical when the filter expands the data, as happens with a decompression filter. If the output is not larger than the input, the filter might process the data in place, without moving it to a new buffer. In that case, two or more pin connections can share one allocator.

Delivering Samples

Input pins that support local memory expose the IMemInputPin interface. The output pin delivers a sample by calling the IMemInputPin::Receive method on the input pin. The input pin does one of the following:

The IMemInputPin::ReceiveCanBlock method determines whether the input pin might block on the call to Receive. The output pin can call this method to determine an appropriate threading strategy. Some filters create a worker thread, so that they can deliver samples in the background while doing other work. Other filters simply block until the downstream filter is ready to accept another sample.

There is also a method for delivering more than one sample at a time, IMemInputPin::ReceiveMultiple. It works like Receive, but with an array of samples.

Stop, Pause, and Run

Filters have three states—stopped, paused, and running. For most filters, paused and running are equivalent:

Renderer filters are an exception. When a renderer filter goes from stopped to paused, it does not complete the transition until it receives one media sample. At that point, it holds the sample and blocks on the call to Receive. Video renderers paint the sample as a still frame. Audio renderers do not render the sample until they run. (There is nothing equivalent to a "still frame" for audio.) In either case, the renderer does not accept any more samples until it goes from paused to running.

The filter graph manager controls the state of the filter graph as a whole. The valid state transitions are between stopped and paused, and between paused and running. Transitions between stopped and running must go through paused. (If you call IMediaControl::Run on a stopped graph, or IMediaControl::Stop on a running graph, the filter graph manager pauses the graph first.)

When the filter graph goes from stopped to paused, the following sequence occurs:

These events can take an arbitrary amount of time to complete—usually not much, but the delay could be noticeable, especially if the source requires decompression. Once the graph has finished pausing, however, it is able to start rendering data immediately. Thus, pausing the graph has the effect of "cueing" the data. An application can do this ahead of time, and then quickly switch the graph to running, in response to a command by the user.

Live Capture

Unlike other source filters, live capture filters do not deliver samples while paused. Between the pause command and the run command, any amount of time might elapse. Samples that were delivered at the time of the pause command would be outdated. If the user waits for an hour before issuing the run command, the graph should produce new video frames, not frames that were captured an hour ago. Therefore, capture filters deliver samples only when running.

However, this behavior could cause a problem for the graph. Before the filter graph manager runs the graph, it waits for all of the filters to pause. But the renderer never completes this transition, because it is waiting for a sample from the capture filter. To get around this problem, the capture filter's IMediaFilter::GetState method returns VFW_S_CANT_CUE. This return code notifies the filter graph manager that the capture filter is not delivering samples, and therefore not to wait on the renderer.

Further Reading

For more information about data flow, see Data Flow for Filter Developers.