Asynchronous MFTs

This topic describes asynchronous data processing for Media Foundation transforms (MFTs).

Note

This topic applies to Windows 7 or later.

 

About Asynchronous MFTs

When MFTs were introduced in Windows Vista, the API was designed for synchronous data processing. In that model, the MFT is always either waiting to get input, or waiting to produce output.

Consider a typical video decoder. To get a decoded frame, the client calls IMFTransform::ProcessOutput. If the decoder has enough data to decode a frame, ProcessOutput blocks while the MFT decodes the frame. Otherwise, ProcessOutput returns MF_E_TRANSFORM_NEED_MORE_INPUT, indicating that the client should call IMFTransform::ProcessInput.

This model works well if the decoder performs all of its decoding operations on one thread. But suppose the decoder uses several threads to decode frames in parallel. For the best performance, the decoder should receive new input whenever a decoding thread becomes idle. But the rate at which threads complete decoding operations will not align exactly with the client's calls to ProcessInput and ProcessOutput, resulting in threads waiting for work.

Windows 7 introduces event-driven, asynchronous processing for MFTs. In this model, whenever the MFT needs input or has output, it sends an event to the client.

General Requirements

This topic describes how asynchronous MFTs differ from synchronous MFT. Except where noted in this topic, the two processing models are the same. (In particular, format negotiation is the same.)

An asynchronous MFT must implement the following interfaces:

Events

An asynchronous MFT uses the following events to signal its data-processing status:

Event Description
METransformNeedInput Sent when the MFT can accept more input.
METransformHaveOutput Sent when the MFT has output.
METransformDrainComplete Sent when a drain operation completes. See Draining.
METransformMarker Sent when a marker is process. See Markers.

 

These events are sent out-of-band. It is important to understand the difference between in-band and out-of-band events in the context of an MFT.

The original MFT design supports in-band events. An in-band event contains information about the data stream, such as information about a format change. The client sends in-band events to the MFT by calling IMFTransform::ProcessEvent. The MFT can send in-band events back to the client in the ProcessOutput method. (Specifically, events are conveyed in the pEvents member of the MFT_OUTPUT_DATA_BUFFER structure.)

An MFT sends out-of-band events through the IMFMediaEventGenerator interface as follows:

  1. The MFT implements the IMFMediaEventGenerator interface, as described in Media Event Generators.
  2. The client calls IUnknown::QueryInterface on the MFT for the IMFMediaEventGenerator interface. An asynchronous MFT must expose this interface. Synchronous MFTs should not expose this interface.
  3. The client calls IMFMediaEventGenerator::BeginGetEvent and IMFMediaEventGenerator::EndGetEvent to receive out-of-band events from the MFT.

ProcessInput

The IMFTransform::ProcessInput method is modified as follows:

  1. When streaming starts, the client sends the MFT_MESSAGE_NOTIFY_START_OF_STREAM message.
  2. During streaming, the MFT requests data by sending an METransformNeedInput event. The event data is the stream identifier.
  3. For each METransformNeedInput event, the client calls ProcessInput for the specified stream.
  4. At the end of streaming, the client may call ProcessMessage with the MFT_MESSAGE_NOTIFY_END_OF_STREAM message.

Implementation notes:

ProcessOutput

The IMFTransform::ProcessOutput method is modified as follows:

  1. Whenever the MFT has output, it sends an METransformHaveOutput event.
  2. For each METransformHaveOutput event, the client calls ProcessOutput.

Implementation notes:

  • If the client calls ProcessOutput at any other time, the method returns E_UNEXPECTED.
  • An asynchronous MFT should never return MF_E_TRANSFORM_NEED_MORE_INPUT from the ProcessOutput method. If the MFT requires more input, it sends an METransformNeedInput event.

Draining

Draining an MFT causes the MFT to produce as much output as it can from whatever input data has already been sent. Draining an asynchronous MFT works as follows:

  1. The client sends the MFT_MESSAGE_COMMAND_DRAIN message.
  2. The MFT continues to send METransformHaveOutput events until it has no more data to process. It does not send METransformNeedInput events during this time.
  3. After the MFT sends the last METransformHaveOutput event, it sends an METransformDrainComplete event.

After draining is complete, the MFT does not send another METransformNeedInput event until it receives an MFT_MESSAGE_NOTIFY_START_OF_STREAM message from the client.

Flushing

The client can flush the MFT by sending the MFT_MESSAGE_COMMAND_FLUSH message. The MFT drops all of the input and output samples it is holding.

The MFT does not send another METransformNeedInput event until it receives an MFT_MESSAGE_NOTIFY_START_OF_STREAM message from the client.

Markers

The client can mark a point in the stream by sending the MFT_MESSAGE_COMMAND_MARKER message. The MFT responds as follows:

  1. The MFT generates as many output samples as it can from the existing input data, sending an METransformHaveOutput event for each output sample.
  2. After all of the output is generated, the MFT sends an METransformMarker event. This event must be sent after all of the METransformHaveOutput events.

For example, suppose that a decoder has enough input data to produce four output samples. If the client sends the MFT_MESSAGE_COMMAND_MARKER message, the MFT will queue four METransformHaveOutput events (one per output sample), followed by an METransformMarker event.

The marker message is similar to the drain message. However, a drain is considered a break in the stream, whereas a marker is not. Draining and markers have the following differences.

Draining:

  • While draining, the MFT does not send METransformNeedInput events.
  • The MFT discards any input data that cannot be used to create an output sample.
  • Some MFTs produce a "tail" at the end of the data. For example, audio effects such as reverb or echo produce extra data after the input data has stopped. An MFT that generates a tail should do so at the end of a drain operation.
  • After the MFT has finished draining, it marks the next output sample with the MFSampleExtension_Discontinuity attribute, to indicate a discontinuity in the stream.

Marker:

  • The MFT continues to send METransformNeedInput events before sending the marker event.
  • The MFT does not discard any input data. If there is partial data, it should be processed after the marker point.
  • The MFT does not produce a tail at the marker point.
  • The MFT does not set the discontinuity flag after the marker point.

Format Changes

An asynchronous MFT must support dynamic format changes, as described in Handling Stream Changes.

Attributes

An asynchronous MFT must implement the IMFTransform::GetAttributes method to return a valid attribute store. The following attributes apply to asynchronous MFTs:

Attribute Description
MF_TRANSFORM_ASYNC The MFT must set this attribute to TRUE (1). The client can query this attribute to discover whether the MFT is asynchronous.
MF_TRANSFORM_ASYNC_UNLOCK
MFT_SUPPORT_DYNAMIC_FORMAT_CHANGE The MFT must set this attribute to TRUE (1). The client can assume that this attribute is set.

 

Unlocking Asynchronous MFTs

Asynchronous MFTs are not compatible with the original MFT data processing model. To prevent asynchronous MFTs from breaking existing applications, the following mechanism is defined:

The client calls IMFTransform::GetAttributes on the MFT. The client queries the for this MF_TRANSFORM_ASYNC attribute. For an asynchronous MFT, the value of this attribute is **TRUE**. To unlock the MFT, the client must sets the MF_TRANSFORM_ASYNC_UNLOCK attribute to **TRUE**.

Until the client unlocks the MFT, all IMFTransform methods should return MF_E_TRANSFORM_ASYNC_LOCKED, with the following exceptions:

The following code shows how to unlock an asynchronous MFT:

HRESULT UnlockAsyncMFT(IMFTransform *pMFT)
{
    IMFAttributes *pAttributes = NULL;

    HRESULT hr = hr = pMFT->GetAttributes(&pAttributes);

    if (SUCCEEDED(hr))
    {
        hr = pAttributes->SetUINT32(MF_TRANSFORM_ASYNC_UNLOCK, TRUE);
        pAttributes->Release();
    }
    
    return hr;
}

Shutting Down the MFT

Asynchronous MFTs must implement the IMFShutdown interface.

  • Shutdown: The MFT must shut down its event queue. If using the standard event queue, call IMFMediaEventQueue::Shutdown. Optionally, the MFT may release other resources. The client must not use the MFT after calling Shutdown.
  • GetShutdownStatus: After Shutdown has been called, the MFT should return the value MFSHUTDOWN_COMPLETED in the pStatus parameter. It should not return the value MFSHUTDOWN_INITIATED.

Registration and Enumeration

To register an asynchronous MFT, call the MFTRegister function and set the MFT_ENUM_FLAG_ASYNCMFT flag in the Flags parameter. (Previously this flag was reserved.)

To enumerate asynchronous MFTs, call the MFTEnumEx function and set the MFT_ENUM_FLAG_ASYNCMFT flag in the Flags parameter. For backward compatibility, the MFTEnum function does not enumerate asynchronous MFTs. Otherwise, installing an asynchronous MFT on the user's computer could break existing applications.

Media Foundation Transforms