Microsoft DirectX 9.0 |
Filters have three possible states: stopped, paused, and running. The purpose of the paused state is to cue data in the graph, so that a run command responds immediately. The Filter Graph Manager controls all state transitions. When an application calls IMediaControl::Run, IMediaControl::Pause, or IMediaControl::Stop, the Filter Graph Manager calls the corresponding IMediaFilter method on all of the filters. Transitions between stopped and running always go through the paused state, so if the application calls Run on a stopped graph, the Filter Graph Manager pauses the graph before running it.
For most filters, the running and paused states are identical. Consider the following filter graph:
Source > Transform > Renderer
Assume for now that the source filter is not a live capture source. When the source filter pauses, it creates a thread that generates new data and writes it into media samples as quickly as possible. The thread "pushes" the samples downstream by calling IMemInputPin::Receive on the transform filter's input pin. The transform filter receives the samples on the source filter's thread. It may use a worker thread to deliver the samples to the renderer, but typically it delivers them on the same thread. While the renderer is paused, it waits to receive a sample. After it receives one, it blocks and holds that sample indefinitely. If it is a video renderer, it displays the sample as a poster image, repainting the image as necessary.
At this point, the stream is fully cued and ready for rendering. If the graph remains paused, samples will "pile up" in the graph behind the first sample, until every filter is blocked in Receive or GetBuffer. No data is lost, though. Once the source thread is unblocked, it simply resumes from the point where it blocked.
The source filter and the transform filter ignore the transition from paused to running—they simply continue to process data as fast as possible. But when the renderer runs, it starts rendering samples. First it renders the sample it held while it was paused. Then, each time it receives a new sample, it calculates the sample's presentation time. (For details, see Time and Clocks in DirectShow.) The renderer holds each sample until the presentation time, at which point it renders the sample. While it waits for the presentation time, it either blocks in the Receive method, or receives new samples on a worker thread with a queue. Filters upstream from the renderer are not involved in scheduling.
Live sources, such as capture devices, are an exception to this general architecture. With a live source, it is not appropriate to cue any data in advance. The application might pause the graph and then wait for a long time before running it. The graph should not render "stale" samples. Therefore, a live source produces no samples while paused, only while running. To signal this fact to the Filter Graph Manager, the source filter's IMediaFilter::GetState method returns VFW_S_CANT_CUE. This return code indicates that the filter has switched to the paused state, even though the renderer did not receive any data.
When a filter stops, it rejects any more samples delivered to it. Source filters shut down their streaming threads, and other filters shut down any worker threads they may have created. Pins decommit their allocators.
State Transitions
The Filter Graph Manager carries out all state transitions in upstream order, starting from the renderer and working backward to the source filter. This ordering is necessary to prevent samples from being dropped and to prevent the graph from deadlocking. The most crucial state transitions are between paused and stopped: