共用方式為


建立播放拓撲

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

建立拓撲

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

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

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

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

將串流連線到媒體匯聚器

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

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

//  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 播放媒體檔案

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

媒體會話

拓撲