再生トポロジの作成

このトピックでは、オーディオまたはビデオ再生用のトポロジを作成する方法について説明します。 基本的な再生では、ソース ノードが出力ノードに直接接続される部分トポロジを作成できます。 デコーダーやカラー コンバーターなど、中間変換用のノードを挿入する必要はありません。 メディア セッションはトポロジ ローダーを使用してトポロジを解決し、トポロジ ローダーは必要な変換を挿入します。

トポロジの作成

メディア ソースから部分再生トポロジを作成するための全体的な手順を次に示します。

  1. メディア ソースを作成します。 ほとんどの場合、ソース リゾルバーを使用してメディア ソースを作成します。 詳細については、「 ソース リゾルバー」を参照してください。
  2. メディア ソースのプレゼンテーション記述子を取得します。
  3. 空のトポロジを作成します。
  4. ストリーム記述子を列挙するには、プレゼンテーション記述子を使用します。 ストリーム記述子ごとに次の手順を実行します。
    1. オーディオやビデオなど、ストリームの主要なメディアの種類を取得します。
    2. ストリームが現在選択されているかどうかを確認します。 (必要に応じて、メディアの種類に基づいてストリームを選択または選択解除できます)。
    3. ストリームが選択されている場合は、ストリームのメディアの種類に基づいて、メディア シンクのアクティブ化オブジェクトを作成します。
    4. ストリームのソース ノードとメディア シンクの出力ノードを追加します。
    5. ソース ノードを出力ノードに接続します。

このプロセスを簡単に進めるために、このトピックのコード例は複数の関数に編成されています。 最上位の関数の名前 CreatePlaybackTopologyは です。 次の 3 つのパラメーターを受け取ります。

  • メディア ソースの IMFMediaSource インターフェイスへのポインター。
  • プレゼンテーション記述子の IMFPresentationDescriptor インターフェイスへのポインター。 IMFMediaSource::CreatePresentationDescriptor を呼び出して、このポインターを取得します。 複数のプレゼンテーションを含むソースの場合、後続のプレゼンテーションのプレゼンテーション記述子は MENewPresentation イベントで配信されます。
  • アプリケーション ウィンドウへのハンドル。 ソースにビデオ ストリームがある場合は、このウィンドウにビデオが表示されます。

関数は、 ppTopology パラメーターの部分再生トポロジへのポインターを返します。

//  Create a playback topology from a media source.
HRESULT CreatePlaybackTopology(
    IMFMediaSource *pSource,          // Media source.
    IMFPresentationDescriptor *pPD,   // Presentation descriptor.
    HWND hVideoWnd,                   // Video window.
    IMFTopology **ppTopology)         // Receives a pointer to the topology.
{
    IMFTopology *pTopology = NULL;
    DWORD cSourceStreams = 0;

    // Create a new topology.
    HRESULT hr = MFCreateTopology(&pTopology);
    if (FAILED(hr))
    {
        goto done;
    }




    // Get the number of streams in the media source.
    hr = pPD->GetStreamDescriptorCount(&cSourceStreams);
    if (FAILED(hr))
    {
        goto done;
    }

    // For each stream, create the topology nodes and add them to the topology.
    for (DWORD i = 0; i < cSourceStreams; i++)
    {
        hr = AddBranchToPartialTopology(pTopology, pSource, pPD, i, hVideoWnd);
        if (FAILED(hr))
        {
            goto done;
        }
    }

    // Return the IMFTopology pointer to the caller.
    *ppTopology = pTopology;
    (*ppTopology)->AddRef();

done:
    SafeRelease(&pTopology);
    return hr;
}

この関数では、次の手順を実行します。

  1. MFCreateTopology を呼び出してトポロジを作成します。 最初は、トポロジにノードが含まれていません。
  2. プレゼンテーション内のストリームの数を取得するには、 IMFPresentationDescriptor::GetStreamDescriptorCount を呼び出します。
  3. ストリームごとに、トポロジ内のブランチに対してアプリケーション定義 AddBranchToPartialTopology 関数を呼び出します。 この関数は、次のセクションで示します。

メディア シンクへのストリームの接続

選択したストリームごとに、ソース ノードと出力ノードを追加し、2 つのノードを接続します。 ソース ノードはストリームを表します。 出力ノードは、 拡張ビデオ レンダラー (EVR) または ストリーミング オーディオ レンダラー (SAR) のいずれかを表します。

次の例に示す 関数は AddBranchToPartialTopology 、次のパラメーターを受け取ります。

  • トポロジの IMFTopology インターフェイスへのポインター。
  • メディア ソースの IMFMediaSource インターフェイスへのポインター。
  • プレゼンテーション記述子の IMFPresentationDescriptor インターフェイスへのポインター。
  • ストリームの 0 から始まるインデックス。
  • ビデオ ウィンドウへのハンドル。 このハンドルは、ビデオ ストリームにのみ使用されます。
//  Add a topology branch for one stream.
//
//  For each stream, this function does the following:
//
//    1. Creates a source node associated with the stream. 
//    2. Creates an output node for the renderer. 
//    3. Connects the two nodes.
//
//  The media session will add any decoders that are needed.

HRESULT AddBranchToPartialTopology(
    IMFTopology *pTopology,         // Topology.
    IMFMediaSource *pSource,        // Media source.
    IMFPresentationDescriptor *pPD, // Presentation descriptor.
    DWORD iStream,                  // Stream index.
    HWND hVideoWnd)                 // Window for video playback.
{
    IMFStreamDescriptor *pSD = NULL;
    IMFActivate         *pSinkActivate = NULL;
    IMFTopologyNode     *pSourceNode = NULL;
    IMFTopologyNode     *pOutputNode = NULL;

    BOOL fSelected = FALSE;

    HRESULT hr = pPD->GetStreamDescriptorByIndex(iStream, &fSelected, &pSD);
    if (FAILED(hr))
    {
        goto done;
    }

    if (fSelected)
    {
        // Create the media sink activation object.
        hr = CreateMediaSinkActivate(pSD, hVideoWnd, &pSinkActivate);
        if (FAILED(hr))
        {
            goto done;
        }

        // Add a source node for this stream.
        hr = AddSourceNode(pTopology, pSource, pPD, pSD, &pSourceNode);
        if (FAILED(hr))
        {
            goto done;
        }

        // Create the output node for the renderer.
        hr = AddOutputNode(pTopology, pSinkActivate, 0, &pOutputNode);
        if (FAILED(hr))
        {
            goto done;
        }

        // Connect the source node to the output node.
        hr = pSourceNode->ConnectOutput(0, pOutputNode, 0);
    }
    // else: If not selected, don't add the branch. 

done:
    SafeRelease(&pSD);
    SafeRelease(&pSinkActivate);
    SafeRelease(&pSourceNode);
    SafeRelease(&pOutputNode);
    return hr;
}

関数は次の処理を行います。

  1. IMFPresentationDescriptor::GetStreamDescriptorByIndex を呼び出し、ストリーム インデックスを渡します。 このメソッドは、ストリームが選択されているかどうかを示すブール値と共に、そのストリームのストリーム記述子へのポインターを返します。
  2. ストリームが選択されていない場合、この関数は終了し、S_OKを返します。これは、選択されていない限り、アプリケーションでストリームのトポロジ ブランチを作成する必要がないためです。
  3. ストリームが選択されている場合、関数はトポロジ ブランチを次のように完了します。
    1. アプリケーション定義の CreateMediaSinkActivate 関数を呼び出して、シンクのアクティブ化オブジェクトを作成します。 この関数は、次のセクションで示します。
    2. トポロジにソース ノードを追加します。 この手順のコードは、「 ソース ノードの作成」トピックに示されています。
    3. トポロジに出力ノードを追加します。 この手順のコードは、 出力ノードの作成に関するトピックに示されています。
    4. ソース ノードで IMFTopologyNode::ConnectOutput を呼び出して、2 つのノードを接続します。 ノードを接続することで、アプリケーションはアップストリーム ノードがダウンストリーム ノードにデータを配信する必要があることを示します。 ソース ノードには 1 つの出力があり、出力ノードには 1 つの入力があるため、両方のストリーム インデックスは 0 です。

より高度なアプリケーションでは、ソースの既定の構成を使用する代わりに、ストリームを選択または選択解除できます。 1 つのソースに複数のストリームを含めることができます。既定では、いずれかのストリームが選択されている可能性があります。 メディア ソースのプレゼンテーション記述子には、ストリーム選択の既定のセットがあります。 単一のオーディオ ストリームとビデオ ストリームを含む単純なビデオ ファイルでは、通常、メディア ソースは既定で両方のストリームを選択します。 ただし、ファイルには、異なる言語の複数のオーディオ ストリーム、または異なるビット レートでエンコードされた複数のビデオ ストリームが含まれます。 その場合、一部のストリームは既定で選択されません。 アプリケーションは、プレゼンテーション記述子で IMFPresentationDescriptor::SelectStreamIMFPresentationDescriptor::D eselectStream を呼び出すことによって選択を変更できます。

メディア シンクの作成

次の関数は、EVR または SAR メディア シンクのアクティブ化オブジェクトを作成します。

//  Create an activation object for a renderer, based on the stream media type.

HRESULT CreateMediaSinkActivate(
    IMFStreamDescriptor *pSourceSD,     // Pointer to the stream descriptor.
    HWND hVideoWindow,                  // Handle to the video clipping window.
    IMFActivate **ppActivate
)
{
    IMFMediaTypeHandler *pHandler = NULL;
    IMFActivate *pActivate = NULL;

    // Get the media type handler for the stream.
    HRESULT hr = pSourceSD->GetMediaTypeHandler(&pHandler);
    if (FAILED(hr))
    {
        goto done;
    }

    // Get the major media type.
    GUID guidMajorType;
    hr = pHandler->GetMajorType(&guidMajorType);
    if (FAILED(hr))
    {
        goto done;
    }
 
    // Create an IMFActivate object for the renderer, based on the media type.
    if (MFMediaType_Audio == guidMajorType)
    {
        // Create the audio renderer.
        hr = MFCreateAudioRendererActivate(&pActivate);
    }
    else if (MFMediaType_Video == guidMajorType)
    {
        // Create the video renderer.
        hr = MFCreateVideoRendererActivate(hVideoWindow, &pActivate);
    }
    else
    {
        // Unknown stream type. 
        hr = E_FAIL;
        // Optionally, you could deselect this stream instead of failing.
    }
    if (FAILED(hr))
    {
        goto done;
    }
 
    // Return IMFActivate pointer to caller.
    *ppActivate = pActivate;
    (*ppActivate)->AddRef();

done:
    SafeRelease(&pHandler);
    SafeRelease(&pActivate);
    return hr;
}

この関数では、次の手順を実行します。

  1. ストリーム記述子 で IMFStreamDescriptor::GetMediaTypeHandler を呼び出します。 このメソッドは、 IMFMediaTypeHandler インターフェイス ポインターを返します。

  2. IMFMediaTypeHandler::GetMajorType を呼び出します。 このメソッドは、ストリームのメジャー型 GUID を返します。

  3. ストリームの種類がオーディオの場合、関数は MFCreateAudioRendererActivate を呼び出して、オーディオ レンダラーのアクティブ化オブジェクトを作成します。 ストリームの種類が video の場合、関数は MFCreateVideoRendererActivate を呼び出して、ビデオ レンダラーのアクティブ化オブジェクトを作成します。 これらの関数はどちらも 、IMFActivate インターフェイスへのポインターを返します。 このポインターは、前に示したように、シンクの出力ノードを初期化するために使用されます。

その他のストリームの種類については、この例ではエラー コードを返します。 または、ストリームの選択を解除することもできます。

次の手順

一度に 1 つのメディア ファイルを再生するには、 IMFMediaSession::SetTopology を呼び出して、メディア セッションでトポロジをキューに入れます。 メディア セッションはトポロジ ローダーを使用してトポロジを解決します。 完全な例については、「 Media Foundation でメディア ファイルを再生する方法」を参照してください。

保護されていないメディア ファイルを再生する方法

メディア セッション

トポロジ