Microsoft DirectX 8.0

DirectX Media Object の使い方

ここでは、アプリケーションで Microsoft® DirectX® Media Object (DMO) を使う方法を説明する。このトピックの説明は、DMO に直接アクセスするアプリケーションに対してのみ当てはまる。Microsoft® DirectShow® フィルタ グラフで DMO を使用する場合は、DMO ラッパー フィルタを使用する。

以下の項目について説明する。

ストリームとバッファ

概念的にいえば、DMO は、m 個の入力を受け取り、n 個の出力を生成するオブジェクトである。入力と出力は "ストリーム" と呼ばれる。すべての DMO には少なくとも 1 つのストリームがある。入力ストリームのない DMO や出力ストリームのない DMO もあるが、通常の DMO には入力と出力の両方がある。

  DirectShow のピンと異なり、ストリームは個別の COM オブジェクトではない。ストリームは DMO オブジェクト自体からゼロベース インデックスを使って参照される。

DMO は入力ストリームを通じてデータを受け取る。DMO はデータを処理し、出力ストリームを通じて出力を生成する。すべてのデータは "メディア タイプ" によって分類される。メディア タイプは、データの内容を解釈する方法を定義する。たとえば、320 x 240 24 ビット RGB は 1 つのタイプであり、44.1KHz 16 ビット ステレオ PCM オーディオは別のタイプである。メディア タイプは、DMO_MEDIA_TYPE 構造体を使って記述される。

DMO の各ストリームは、特定の範囲のメディア タイプを受け入れることができる。DMO によって、受け入れるタイプの範囲が広いことも (たとえば任意のビット深度のビデオ)、狭いこともある (たとえば 16 ビット ビデオのみ)。また、特定の入力と出力の組み合わせに限定された DMO もある。たとえば、入力ストリームが 16 ビット ビデオに設定されている場合は、出力ストリームのビット深度を同じにしなければならないことがある。アプリケーションは、各ストリームの優先タイプを列挙した後、特定の組み合わせをテストすることができる。

例:

アプリケーションは、メモリ ブロックごとにグループ分けされた入力データを配布する。個々のメモリ ブロックは、IMediaBuffer インターフェイスをサポートする "バッファ" と呼ばれる COM オブジェクトによってカプセル化される。このインターフェイスは、バッファ内のデータ長を設定するメソッド、データを指すポインタを取得するメソッド、および割り当てられたバッファのサイズを取得するメソッドを備えている。アプリケーションは、DMO が入力と出力の両方に使用するすべてのバッファを割り当てる役割を担う。

データの流れ

ここでは、データがアプリケーションと DMO の間で、どのように移動するかを説明する。

アプリケーションは、入力バッファの配布と出力の要求を交互に行う。アプリケーションは、DMO に対して IMediaObject::ProcessInput メソッドを呼び出すことによって入力バッファを配布し、IMediaObject::ProcessOutput メソッドを呼び出すことによって出力を要求する。

入力データの流れを止めるには 2 つの方法がある。

ストリーミングは、アプリケーションが最初に DMO に対して IMediaObject::ProcessInput を呼び出したときに始まる。DMO は、アプリケーションがすべてのストリームのメディア タイプを設定するまで、ストリーミングを開始しない。(オプション ストリームは例外である。「破棄可能ストリームとオプション ストリーム」を参照すること。) アプリケーションが DMO をフラッシュするか、すべての入力ストリームの途切れを通知した後、すべての出力を処理すると、ストリーミングが停止する。これらの動作によって DMO は非ストリーミング状態に戻る。DMO はすべてのメディア タイプの設定を保持するが、すべての IMediaBuffer ポインタを解放する。それ以降、アプリケーションが少なくとも 1 回 ProcessInput を呼び出すまで、DMO は出力を生成できない。

データをストリーミングするには、以下の手順を実行する。

  1. DMO がサポートするストリームの数と各ストリームの優先メディア タイプを DMO に問い合わせる。
  2. すべてのストリームのメディア タイプを設定する。
  3. 入力バッファと出力バッファを割り当てる。
  4. 入力バッファにデータを格納し、ProcessInput を呼び出す。
  5. ProcessOutput を呼び出し、出力データを取得する。すべての入力データが処理されるまで、手順 4 〜 5 を繰り返す。
  6. 途切れを通知し、残っているすべての出力を処理する。

データをフラッシュすることによって手順 4 〜 5 を中断できる。

データを処理する方法

ここでは、前のセクションで示した手順を詳しく説明する。

手順 1: DMO に対する問い合わせ

まず、DMO がサポートするストリームの数と各ストリームの優先メディア タイプを DMO に問い合わせる。入力ストリームと出力ストリームの数を取得するには、IMediaObject::GetStreamCount メソッドを呼び出す。

各ストリームについて、DMO は優先順位に従って優先メディア タイプのランク付けを行い、各タイプにゼロから始まるインデックスを割り当てる。特定のストリームの優先メディア タイプを取得するには、IMediaObject::GetInputType メソッドまたは IMediaObject::GetOutputType メソッドを呼び出す。ストリーム番号とメディア タイプ インデックスを指定する。ストリームのすべてのメディア タイプを列挙するには、以下の疑似コードに示すように、メソッドが DMO_E_NO_MORE_ITEMS を返すまで、メディア タイプ インデックスをインクリメントするループを使用する。

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++
    }
}

GetInputType メソッドと GetOutputType メソッドは、メディア タイプと共に DMO_MEDIA_TYPE 構造体を返す。以下の構造体メンバが関係する。

メディア タイプは、formattype メンバの値 GUID_NULL によって表される NULL フォーマット構造体を持っていることがある。NULL フォーマットは、DMO が、指定されたメディア タイプの一定の範囲内のフォーマットを受け入れられることを示す。たとえば、PCM オーディオを必要とするストリームは、一定の範囲のサンプリング レートを受け入れることがある。したがって、そのストリームは、メジャー タイプを MEDIATYPE_Audio に、サブタイプを MEDIASUBTYPE_PCMAudio にして、NULL フォーマットを返す。

アプリケーションは、MoFreeMediaType 関数を呼び出して pbFormat メンバを解放する必要がある。

手順 2: メディア タイプの設定

DMO の優先メディア タイプがわかったら、IMediaObject::SetInputType メソッドと IMediaObject::SetOutputType メソッドを呼び出して、各ストリームのメディア タイプを設定する。

DMO が報告するすべてのメディア タイプの組み合わせが有効であるとは限らない。たとえば、出力タイプを入力タイプに合わせなければならないことがある。DMO_SET_TYPEF_TEST_ONLY フラグを設定して SetInputType または SetOutputType を呼び出すことにより、メディア タイプをテストできる。デコーダの場合は、通常、まず入力タイプを設定し、次に出力タイプを選択する。エンコーダの場合は、まず出力タイプを設定し、次に入力タイプを選択する。

1 つのストリームの設定が別のストリームに影響を及ぼすことがあるため、前に設定したメディア タイプを消去する必要が生じることがある。これを行うには、DMO_SET_TYPEF_CLEAR フラグを設定して SetInputType または SetOutputType を呼び出す。

手順 3: バッファの割り当て

メディア タイプを設定したら、各ストリームのバッファ要件を DMO に問い合わせる。バッファ要件はメディア タイプによって異なることがある。各ストリームについて、IMediaObject::GetInputSizeInfo メソッドまたは IMediaObject::GetOutputSizeInfo メソッドを呼び出す。これらのメソッドは、以下の 3 つの値を返す。

これらの要件を満たすのに十分なバッファを割り当てる必要がある。

サイズ要件のほかに、入力ストリームでは、各バッファにサンプル全体または 1 つのサンプルだけが含まれていることや、各バッファが一定のサンプル サイズを使用することが必要とされることがある。これらの要件を調べるには、IMediaObject::GetInputStreamInfo メソッドを呼び出す。

手順 4: 入力の処理

この時点で、入力バッファを DMO に配布できる。

各入力ストリームについて、1 つ以上のバッファにメディア データを格納する。データを直接バッファに書き込むことも、別の DMO の出力バッファを使うこともできる。ProcessInput メソッドを呼び出して、各バッファを配布する。通常、DMO はバッファの参照カウントを保持する。DMO は、生成できるすべての出力を生成したとき、またはアプリケーションが DMO をフラッシュしたときに、バッファを解放する。DMO がバッファを解放するまで、バッファを再利用しないこと。

入力ストリームがデータをこれ以上受け入れられるかどうかを判別するには、IMediaObject::GetInputStatus メソッドを呼び出す。ストリームがデータをこれ以上受け入れられる場合、メソッドは DMO_INPUT_STATUSF_ACCEPT_DATA フラグを返す。

手順 5: 出力の処理

ProcessInput は一度に 1 つずつ入力バッファを配布するが、ProcessOutput はいっぺんにすべての出力ストリームの出力を生成する。アプリケーションは、各出力ストリームに 1 つの構造体を割り当てて、DMO_OUTPUT_DATA_BUFFER 構造体の配列を渡す。構造体には、アプリケーションが割り当てた出力バッファおよび DMO がデータを格納するさまざまなフィールドが含まれている。

ProcessOutput メソッドにより、DMO は、指定された出力バッファのサイズの範囲内で、できるだけ多くのデータを生成する。すべてのデータを処理するまえに出力バッファがいっぱいになった場合、DMO は構造体の dwStatus メンバの DMO_OUTPUT_DATA_BUFFERF_INCOMPLETE フラグを設定する。メソッドが終了したら、各構造体にこのフラグがあるかどうかを確認する。フラグがある場合は、もう一度 ProcessOutput を呼び出す。

ストリーミングが始まると、DMO は常に入力を受け入れるか、出力を生成するか、その両方を行うことができる。したがって、GetInputStatus が DMO_INPUT_STATUSF_ACCEPT_DATA フラグを返すか、ProcessOutput が DMO_OUTPUT_DATA_BUFFERF_INCOMPLETE フラグを返す。アプリケーションは、これらのフラグの有無をテストし、ProcessInput または ProcessOutput を呼び出すことによって、データの流れを維持する。

手順 6: 途切れ (discontinuity) の通知

使用できるすべての入力データを特定の入力ストリームに配布したら、IMediaObject::Discontinuity メソッドを呼び出す。DMO は、残っている出力の処理が終わるまで、そのストリームに対する入力をそれ以上受け入れない。

破棄可能ストリームとオプション ストリーム

DMO は、出力ストリームの一部を破棄可能またはオプション ストリームとして指定できる。

ストリームが破棄可能またはオプションであるかどうかを問い合わせるには、IMediaObject::GetOutputStreamInfo メソッドを呼び出し、pdwFlags パラメータをチェックする。DMO_OUTPUT_STREAMF_DISCARDABLE フラグはストリームが破棄可能であることを示し、DMO_OUTPUT_STREAMF_OPTIONAL フラグはストリームがオプション ストリームであることを示す。一般的に、少なくとも 1 つのストリームは、オプション ストリーム以外のストリームでなければならない。

IMediaObject::ProcessOutput メソッドを呼び出すときは、dwFlags パラメータの DMO_PROCESS_OUTPUT_DISCARD_WHEN_NO_BUFFER フラグを設定することによって、1 つ以上のストリームのデータを破棄できる。破棄したい個々のストリームについて、DMO_OUTPUT_DATA_BUFFER 構造体の pBuffer メンバを NULL に設定する。DMO は、以下の操作によって、これらのストリームのデータの破棄を試みる。

pBuffer メンバは NULL であるが、DMO_PROCESS_OUTPUT_DISCARD_WHEN_NO_BUFFER フラグを設定しなかった場合、DMO は、ストリームが破棄可能またはオプションであっても、データを破棄しない。pBuffer は NULL であるが、データが破棄されなかった場合、DMO は、ストリームにデータが残っていることを示す DMO_OUTPUT_DATA_BUFFERF_INCOMPLETE フラグを送る。出力ストリームを処理した後、その出力ストリームを破棄しないと、DMO がほかの出力ストリームのデータを生成できなくなることがある。

オプション ストリームを絶対に使用しない場合は、オプション ストリームのメディア タイプを設定する必要はない。破棄可能なストリームの場合は、これと異なる。破棄可能なストリームのメディア タイプは、必ず設定する必要がある。オプション ストリームと破棄可能ストリームの機能上の相違は、この点だけである。

インプレイス DMO

一部のデータ変換は、データを直接変更することによって行われる。これは、"インプレイス" 処理と呼ばれる。多くのオーディオ エフェクトやビデオ エフェクトは、インプレイスで行うことができる。インプレイス処理は、別のバッファにデータをコピーする方法より効率がよい。データをインプレイスで処理するには、ProcessInputProcessOutput を別々に呼び出す代わりに、IMediaObjectInPlace::Process メソッドだけを呼び出す。入力データが含まれたバイトの配列を渡す。メソッドが終了すると、バイト配列には出力データが格納される。

IMediaObjectInPlace をサポートする DMO は、すべての IMediaObject メソッドもサポートする必要がある。インプレイス処理を使用するか、別々の入力バッファと出力バッファを作成するかは、選択できる。ただし、2 つの種類の処理を混在させないこと。Process を呼び出すときは、ProcessInput または ProcessOutput を呼び出してはいけないし、その逆を行ってもいけない。

インプレイス DMO は、入力が停止した後で、追加の出力を生成することがある。これは、"エフェクト テール" と呼ばれる。たとえば、残響エフェクトは、入力が無音になった後も継続する。DMO がエフェクト テールを生成する場合、アプリケーションは、テールが完全に処理されるまで、入力バッファをゼロにして Process メソッドを呼び出す必要がある。詳細については、「IMediaObjectInPlace::Process」を参照すること。