Microsoft DirectX 8.0

Using DirectX Media Objects

This article describes how to use a Microsoft® DirectX® Media Object (DMO) in an application. This article applies only to applications that access DMOs directly. To use a DMO in a Microsoft® DirectShow® filter graph, use the DMO Wrapper filter.

This article contains the following sections.

Streams and Buffers

Conceptually, a DMO is an object that takes m inputs and produces n outputs. The inputs and outputs are called streams. Every DMO has at least one stream. It's possible for a DMO to have no input streams or no output streams, but a typical DMO has both inputs and outputs.

Note  Unlike pins in DirectShow, streams are not distinct COM objects. They are referenced on the DMO object itself, using a zero-based index.

A DMO receives data through its input streams. It processes the data and produces output through its output streams. All data is typed using a media type. The media type defines how to interpret the contents of the data. For example, 320 x 240 24-bit RGB video is one type; 44.1-kilohertz (kHz) 16-bit stereo PCM audio is another type. Media types are described using the DMO_MEDIA_TYPE structure.

Each stream on a DMO can accept a certain range of media types. Depending on the DMO, it might accept a wide range of types (video at any bit depth) or a narrow range (only 16-bit video). Also, a DMO might be limited to certain combinations of inputs and outputs. For example, if an input stream is set to 16-bit video, an output stream might require the same bit depth. The application can enumerate each stream's preferred types and then test specific combinations.

Examples:

The application delivers input data grouped in blocks of memory. Each block of memory is encapsulated by a COM object called a buffer that supports the IMediaBuffer interface. This interface contains methods for setting the length of the data in the buffer, retrieving a pointer to the data, and retrieving the allocated size of the buffer. The application is responsible for allocating all buffers used by the DMO, both input and output.

Data Flow

This section describes how data moves between the application and the DMO.

An application alternates between delivering input buffers and requesting output. It delivers input buffers by calling the IMediaObject::ProcessInput method on the DMO. It requests output by calling the IMediaObject::ProcessOutput method.

There are two ways to stop the flow of input data:

Streaming begins when the application first calls IMediaObject::ProcessInput on the DMO. A DMO will not begin streaming until the application sets media types for all the streams. (Optional streams are an exception; see Discardable and Optional Streams.) Streaming stops when the application flushes the DMO, or signals a discontinuity on every input stream and then processes all of the output. These actions return the DMO to a non-streaming state. The DMO retains all of its media type settings, but it releases all IMediaBuffer pointers. At that point, it cannot generate any more output until the application calls ProcessInput at least once.

To stream data, perform the following steps.

  1. Query the DMO for the number of streams it supports and the preferred media types for each stream.
  2. Set media types for all the streams.
  3. Allocate input buffers and output buffers.
  4. Fill the input buffers with data and call ProcessInput.
  5. Call ProcessOutput and retrieve the output data. Repeat steps 4 and 5 until all of the input data is processed.
  6. Signal a discontinuity and process any remaining output.

You can interrupt steps 4 through 6 by flushing the data.

How to Process Data

This section explains in more detail the steps listed in the previous section.

Step 1. Query the DMO

First, query the DMO for the number of streams it supports and the preferred media types for each stream. To retrieve the number of input streams and output streams, call the IMediaObject::GetStreamCount method.

For each stream, the DMO ranks its preferred media types in order of preference and assigns each type an index, starting from zero. To retrieve a preferred media type for a particular stream, call the IMediaObject::GetInputType method or IMediaObject::GetOutputType method. Specify a stream number and a media-type index. To enumerate all the media types on a stream, use a loop that increments the media-type index until the method returns DMO_E_NO_MORE_ITEMS, as shown in the following psuedo-code.

DWORD cInputs, cOutputs, type = 0
DMO_MEDIA_TYPE mt

pDMO->GetStreamCount(&cInputs, &cOutputs)

for (DWORD i = 0; i < cInputs; i++)
{
    while (pDMO->GetInputType(i, type, &mt) != DMO_E_NO_MORE_ITEMS)
    {
        if ( this media type is one you want )
            break
        MoFreeMediaType(&mt)
        type++
    }
}

The GetInputType and GetOutputType methods return a DMO_MEDIA_TYPE structure with the media type. The following structure members are relevant:

The media type might have a NULL format structure, indicated by a value of GUID_NULL for the formattype member. A NULL format indicates that the DMO can accept a range of formats within the specified media type. For example, a stream that requires PCM audio might accept a range of sample rates. Therefore, it returns MEDIATYPE_Audio for the major type, MEDIASUBTYPE_PCMAudio for the subtype, and a NULL format.

The application must call the MoFreeMediaType function to free the pbFormat member.

Step 2. Set Media Types

After finding the DMO's preferred media types, set the media type for each stream by calling the IMediaObject::SetInputType and IMediaObject::SetOutputType methods.

Not every combination of media types reported by the DMO is guaranteed to be valid. For example, the output type might need to match the input type. You can test a media type by calling SetInputType or SetOutputType with the DMO_SET_TYPEF_TEST_ONLY flag. For a decoder, you would generally set the input type and then choose an output type. For an encoder, you would set the output type and then choose an input type.

Because the settings on one stream can affect another stream, you might need to clear a media type that you set previously. To do this, call SetInputType or SetOutputType with the DMO_SET_TYPEF_CLEAR flag.

Step 3. Allocate Buffers

After you set the media types, query the DMO for each stream's buffer requirements. These can change depending on the media type. For each stream, call the IMediaObject::GetInputSizeInfo or IMediaObject::GetOutputSizeInfo method. These methods return three values:

You must allocate sufficient buffers to handle these requirements.

In addition to the size requirements, an input stream might require that each buffer contain only whole samples, or contain exactly one sample, or use a fixed sample size. To determine these requirements, call the IMediaObject::GetInputStreamInfo method.

Step 4. Process Input

At this point, you can deliver input buffers to the DMO.

For each input stream, fill one or more input buffers with media data. You can write data directly into a buffer, or use an output buffer from another DMO. Call the ProcessInput method to deliver each buffer. Typically, the DMO holds a reference count on the buffer. It releases the buffer when it has generated all of the output that it can, or when the application flushes the DMO. Do not re-use the buffer until the DMO releases it.

To determine whether an input stream can accept more data, call the IMediaObject::GetInputStatus method. If the stream can accept more data, the method returns the DMO_INPUT_STATUSF_ACCEPT_DATA flag.

Step 5. Process Output

Whereas ProcessInput delivers one input buffer at a time, ProcessOutput generates output for all the output streams at once. The application passes an array of DMO_OUTPUT_DATA_BUFFER structures, with one structure for every output stream. The structure contains a pointer to an output buffer (allocated by the application) and various fields that are filled in by the DMO.

In the ProcessOutput method, the DMO generates as much data as possible, given the size of the output buffers. If it fills an output buffer before it processes all of the data, it sets the DMO_OUTPUT_DATA_BUFFERF_INCOMPLETE flag in the structure's dwStatus member. When the method returns, check each structure for this flag. If the flag is present, call ProcessOutput again.

After streaming begins, the DMO can always accept input, or produce output, or both. Therefore, either GetInputStatus returns the DMO_INPUT_STATUSF_ACCEPT_DATA flag, or ProcessOutput returns the DMO_OUTPUT_DATA_BUFFERF_INCOMPLETE flag. The application keeps data flowing by testing for these flags and calling ProcessInput or ProcessOutput.

Step 6: Signal a Discontinuity

When you have delivered all available input data to a particular input stream, call the IMediaObject::Discontinuity method. The DMO does not accept further input to that stream until you process any remaining output.

Discardable and Optional Streams

A DMO can designate some of its output streams as discardable or optional:

To query whether a stream is discardable or optional, call the IMediaObject::GetOutputStreamInfo method and check the pdwFlags parameter. The DMO_OUTPUT_STREAMF_DISCARDABLE flag indicates the stream is discardable. The DMO_OUTPUT_STREAMF_OPTIONAL flag indicates that the stream is optional. In general, at least one stream should be non-optional.

When you call the IMediaObject::ProcessOutput method, you can discard data from one or more streams by setting the DMO_PROCESS_OUTPUT_DISCARD_WHEN_NO_BUFFER flag in the dwFlags parameter. For each stream that you want to discard, set the pBuffer member of the DMO_OUTPUT_DATA_BUFFER structure to NULL. The DMO attempts to discard the data from those streams, as follows:

If a pBuffer member is NULL but you do not set the DMO_PROCESS_OUTPUT_DISCARD_WHEN_NO_BUFFER flag, the DMO does not discard the data, even if the stream is discardable or optional. In cases where pBuffer is NULL but no data was discarded, the DMO signals the DMO_OUTPUT_DATA_BUFFERF_INCOMPLETE flag to indicate that the stream still has data. If you do not process an output stream, but do not discard it, the DMO might be unable to generate data for other output streams.

If you will never use an optional stream, you do not have to set its media type. The same is not true for discardable streams; you must always set the media type on a discardable stream. This is the only functional difference between optional and discardable streams.

In-Place DMOs

Certain data transformations can be accomplished by directly modifying the data. This is known as in-place processing. Many audio and video effects can be done in place. In-place processing is more efficient than copying data to another buffer. To process data in-place, make a single call to the IMediaObjectInPlace::Process method, rather than separate calls to ProcessInput and ProcessOutput. Pass an array of bytes that contains the input data. When the method returns, the byte array contains the output data.

A DMO that supports IMediaObjectInPlace must still support all of the IMediaObject methods. You can choose whether to use in-place processing or create separate input and output buffers. However, do not mix the two types of processing. If you call Process, do not call ProcessInput or ProcessOutput, and vice-versa.

An in-place DMO might create some additional output after the input stops. This is known as an effect tail. For example, a reverb effect continues after the input reaches silence. If the DMO produces an effect tail, the application must call the Process method with zeroed input buffers until the tail is completely processed. For more information, see IMediaObjectInPlace::Process.