建立播放拓撲

本主題描述如何建立音訊或視訊播放的拓撲。 若要進行基本播放,您可以建立部分拓撲,其中來源節點會直接連線到輸出節點。 您不需要插入中繼轉換的任何節點,例如解碼器或色彩轉換器。 媒體會話會使用拓撲載入器來解析拓撲,而拓撲載入器將會插入所需的轉換。

建立拓撲

以下是從媒體來源建立部分播放拓撲的整體步驟:

  1. 建立媒體來源。 在大部分情況下,您將使用來源解析程式來建立媒體來源。 如需詳細資訊,請參閱 來源解析程式
  2. 取得媒體來源的簡報描述元。
  3. 建立空的拓撲。
  4. 使用簡報描述項來列舉資料流程描述項。 針對每個資料流程描述項:
    1. 取得資料流程的主要媒體類型,例如音訊或視訊。
    2. 檢查資料流程目前是否已選取。 (選擇性地,您可以根據媒體類型選取或取消選取資料流程。)
    3. 如果選取資料流程,請根據資料流程的媒體類型建立媒體接收的啟用物件。
    4. 新增資料流程的來源節點和媒體接收的輸出節點。
    5. 將來源節點連接到輸出節點。

為了讓此程式更容易遵循,本主題中的範例程式碼會組織成數個函式。 最上層函式的名稱為 CreatePlaybackTopology 。 它會採用三個參數:

函式會傳回 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 函式至拓撲中的分支。 下一節會顯示此函式。

將串流連線到媒體接收

針對每個選取的資料流程,新增來源節點和輸出節點,然後連接兩個節點。 來源節點代表資料流程。 輸出節點代表 增強式視訊轉譯器 (EVR) 或串 流音訊 轉譯器 (SAR) 。

下一個範例所示的 函 AddBranchToPartialTopology 式會採用下列參數:

  • 拓撲之 IMFTopology 介面的指標。
  • 媒體來源之 IMFMediaSource 介面的指標。
  • 表示描述項之 IMFPresentationDescriptor 介面的指標。
  • 資料流程以零起始的索引。
  • 視訊視窗的控制碼。 此控制碼僅用於視訊串流。
//  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 來連接這兩個節點。 藉由連接節點,應用程式會指出上游節點應該將資料傳遞至下游節點。 來源節點有一個輸出,而輸出節點有一個輸入,因此這兩個數據流索引都是零。

更進階的應用程式可以選取或取消選取資料流程,而不是使用來源的預設組態。 來源可以有多個資料流程,而且預設可能會選取其中任何一個資料流程。 媒體來源的簡報描述元有一組預設的資料流程選取專案。 在具有單一音訊資料流程和視訊串流的簡單視訊檔案中,媒體來源通常會預設選取這兩個數據流。 不過,檔案可能會有多個不同語言的音訊資料流程,或以不同位元速率編碼的多個視訊串流。 在此情況下,某些資料流程預設會取消選取。 應用程式可以在簡報描述元上呼叫 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 來建立音訊轉譯器啟用物件。 如果串流類型是視訊,函式會呼叫 MFCreateVideoRendererActivate 來建立視訊轉譯器啟用物件。 這兩個函式都會傳回 IMFActivate 介面的指標。 這個指標是用來初始化接收的輸出節點,如先前所示。

針對任何其他資料流程類型,此範例會傳回錯誤碼。 或者,您可以直接取消選取資料流程。

後續步驟

若要一次播放一個媒體檔案,請呼叫 IMFMediaSession::SetTopology,將媒體會話上的拓撲排入佇列。 媒體會話會使用拓撲載入器來解析拓撲。 如需完整範例,請參閱 如何使用 Media Foundation 播放媒體檔案

如何播放未受保護的媒體檔案

媒體會話

拓撲