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

When a filter's input pin receives a sample, the filter processes the data in the sample and then delivers the sample to the next downstream filter. As the previous section described, the filter can do all of its processing while inside the IMemInputPin::Receiveon the downstream input pin, passing it a pointer to the sample's method, or it can hold the sample and process it afterward.

Within the call to Receive, the input pin has the option of blocking the upstream filter's calling thread. You can query the input pin's behavior by calling the IMemInputPin::ReceiveCanBlockmethod. If the return value is S_OK, the pin might block on a call to Receive. If the return value is S_FALSE, the pin will never block on calls to Receive. Based on the return value, the upstream filter might use a separate worker thread to deliver samples.

Some filters process samples in place, without copying any data. Other filters copy the data before processing it. Obviously, it is better to process samples in place whenever possible, to avoid the overhead of unneeded copy operations.

The input pin can reject samples by returning an error code or S_FALSE in its Receivemethod. The value S_FALSE indicates that the pin is flushing data (see "Flushing" below). An error code indicates some other problem. For example, if the filter is stopped, the return value is VFW_E_WRONG_STATE. If a pin rejects a sample, the upstream filter calls the IPin::EndOfStreammethod and stops sending data. If the pin returned an error code, the upstream filter also signals EC_ERRORABORT to the filter graph manager.

The upstream filter must serialize all of its Receivecalls to a given input pin.

Parsing Media Data

If your filter parses media data, do not trust headers or other self-describing data in the content. For example, do not trust size values that appear in AVI RIFF chunks or MPEG packets. The following list shows common examples of this kind of error:

  • Reading Nbytes of data, where the value of Ncame from the content, without checking Nagainst the actual size of your buffer.

  • Jumping to a byte offset within a buffer, without verifying that the offset falls within the buffer.

Another common class of errors involves not validating format descriptions that are found in the content. The following list shows common examples of this kind of error::

  • A BITMAPINFOHEADERstructure can be followed by a color table. The BITMAPINFOstructure is defined as a BITMAPINFOHEADERstructure followed by an array of RGBQUADvalues that make up the color table. The size of the array is determined by the value of biClrUsed. Never copy a color table into a BITMAPINFOwithout first checking the size of the buffer that was allocated for the BITMAPINFOstructure.

  • A WAVEFORMATEXstructure might have extra format information appended to the structure. The cbSizemember specifies the size of the extra information.

During pin connection, a filter should verify that all format structures are well-formed and contain reasonable values. If not, you should reject the connection. In the code that validates the format structure, be especially careful about arithmetic overflow. For example, in a BITMAPINFOHEADER, the width and height are 32-bit longvalues but the image size (which is a function of the product of the two) is only a DWORDvalue.

If format data from the source is larger than your allocated buffer, do not truncate the source data in order to copy it into your buffer. Doing so can create a structure with an implicit size larger than its actual size. For example, a bitmap header might specify a palette table that no longer exists. Instead of truncating the source data, reallocate the buffer or simply fail the connection.

Errors during Streaming

Your filter should terminate streaming if it receives malformed content while it is running in a filter graph. The following list describes the steps that your filter should take when it terminates streaming.

  • 6Return an error code from Receive.

  • Call IPin::EndOfStreamon the downstream filter.

  • Post an EC_ERROR_ABORT event.

Dynamic Format Changes

Several mechanisms exist for filters to change formats mid-stream. Each of these mechanisms involves more than one step. This creates the potential for false acceptances of the dynamic format change. If your filter gets a request for a dynamic format change from another filter, then it must either reject the request, or else honor the new format when it arrives. Similar results will occur if your filter, changes formats without confirming that the downstream filter agrees.

End of Stream

When a source filter has no more data to send, it calls the IPin::EndOfStreammethod on the downstream input pin. The downstream filter propagates the call to the next filter. Eventually the EndOfStreamcall reaches the renderer filter. The renderer filter signals EC_COMPLETE to the filter graph manager.

Before posting an EC_COMPLETE notification to the application, the filter graph manager waits until all the streams signal EC_COMPLETE. The application does not receive an EC_COMPLETE notice until all the streams have completed. To determine the number of streams in the graph, the filter graph manager counts the number of filters that support IMediaSeekingor IMediaPositionand have a rendered input pin. A rendered input pin is an input pin with no corresponding outputs. The IPin::QueryInternalConnectionsmethod returns zero for a rendered input pin.

Filters must serialize EndOfStreamcalls with IMemInputPin::Receivecalls.

Flushing

Flushing is the process of discarding all the pending samples in the graph. Flushing enables the graph to be more responsive when events alter the normal data flow. For example, in a seek operation, pending data is flushed from the graph before new data is introduced. If the graph contains multiple streams, it is possible to flush individual streams separately.

Flushing is a two-stage process:

  • The source filter calls IPin::BeginFlushon a downstream input pin. The downstream filter flushes any pending data and calls BeginFlushon the next input pin. The call propagates downstream until it reaches the renderer.

  • The source filter calls IPin::EndFlushon the downstream input pin. Again, the downstream filter propagates this call.

When the BeginFlushmethod is called on a filter's input pin, the filter performs the following actions:

  • Returns from any blocking calls to the input pin's IMemInputPin::Receivemethod.

  • Rejects any further samples in calls to Receive, until the EndFlushmethod is called.

  • Discards any samples that were queued for delivery to downstream filters.

  • Clears any pending calls to IPin::EndOfStream.

  • Clears any pending EC_COMPLETE notifications.

  • Calls BeginFlushon downstream input pins.

When the EndFlushmethod is called, the filter performs the following actions:

  • Waits for queued samples to be discarded.

  • Calls EndFlushdownstream.

At this point, the filter can accept samples again.

In the pull model, the parser filter initiates flushing rather than the source filter. The sequence of events is the same, except that the parser also calls BeginFlushand EndFlushon the source filter's output pin.

Critical Sections in Filters

Because filter operations are multithreaded, it is crucial that a filter serialize certain operations. Otherwise, race conditions can result. For example, the filter might try to use an allocator that is already decommitted. Filters use two critical sections, one to hold a streaming lock and the other to hold a filter state lock.

The streaming lock serializes streaming operations. The filter holds this lock when it processes the following method calls:

The filter state lock serializes state changes. The filter holds this lock when it processes the following method calls:

Within the Stopand EndFlushmethods, the filter must synchronize its filter state with its streaming state. In the Stopmethod, the filter holds the filter state lock and decommits the input pin's allocator. Then it holds the streaming lock and decommits the output pin's allocator.

Base Classes for Data Flow

The following base classes are useful for implementing data flow in a filter.

Class Description

COutputQueue

Queues data to be delivered downstream. Uses a worker thread for delivering samples.

CPullPin

Input pin on a parser filter. Uses a worker thread to pull data from a source filter.

CSourceStream

Output pin on a source filter. Uses a worker thread to push data downstream.