建立播放拓撲
本主題描述如何建立音訊或視訊播放的拓撲。 若要進行基本播放,您可以建立部分拓撲,其中來源節點會直接連線到輸出節點。 您不需要插入中繼轉換的任何節點,例如譯碼器或色彩轉換器。 媒體會話會使用拓撲載入器來解析拓撲,而拓撲載入器會插入所需的轉換。
建立拓撲
以下是從媒體來源建立部分播放拓撲的總體步驟:
- 建立媒體來源。 在大部分情況下,您將使用來源解析程式來建立媒體來源。 如需詳細資訊,請參閱 來源解析程式。
- 取得媒體來源的呈現描述符。
- 建立空的拓撲。
- 使用簡報描述元來列舉數據流描述元。 針對每個資料串流描述元:
- 取得串流的主要媒體類型,例如音訊或視訊。
- 檢查資料流是否已經被選取。 (或者,您可以根據媒體類型選取或取消選取串流。)
- 如果選擇數據流,請根據數據流的媒體類型為媒體匯聚建立啟動物件。
- 為數據流新增來源節點,以及媒體接收的輸出節點。
- 將來源節點連接到輸出節點。
為了讓此程式更容易遵循,本主題中的範例程式代碼會組織成數個函式。 最上層函式會命名為 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;
}
此函式會執行下列步驟:
- 呼叫 MFCreateTopology 來建立拓撲。 一開始,拓撲不包含任何節點。
- 呼叫 IMFPresentationDescriptor::GetStreamDescriptorCount,以取得簡報中的數據流數目。
- 針對每個數據流,呼叫應用程式定義的
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;
}
函式會執行下列動作:
- 呼叫 IMFPresentationDescriptor::GetStreamDescriptorByIndex 並傳入數據流索引。 這個方法會傳回該數據流之數據流描述元的指標,以及指出是否已選取數據流的布爾值。
- 如果未選取數據流,函式會結束並傳回S_OK,因為應用程式不需要為數據流建立拓撲分支,除非它已選取。
- 如果選取數據流,函式會完成拓撲分支,如下所示:
- 藉由呼叫應用程式定義的 CreateMediaSinkActivate 函式,建立接收器的激活物件。 下一節會顯示此函式。
- 將來源節點新增至拓撲。 此步驟的程式碼顯示在主題 建立來源節點。
- 將輸出節點新增至拓撲。 此步驟的程式代碼顯示在主題 "建立輸出節點"中。
- 在來源節點上呼叫 IMFTopologyNode::ConnectOutput,以連接這兩個節點。 藉由連接節點,應用程式會指出上游節點應該將數據傳遞給下游節點。 來源節點有一個輸出,而輸出節點有一個輸入,因此兩個數據流索引都是零。
更進階的應用程式可以選取或取消選取數據流,而不是使用來源的預設組態。 來源可以有多個數據流,而且預設可能會選取其中任何一個數據流。 媒體來源的呈現描述子有一組預設的串流選擇。 在具有單一音訊串流和視訊串流的簡單視訊檔案中,媒體來源通常會默認選取這兩個數據流。 不過,檔案可能有多個不同語言的音訊串流,或以不同比特率編碼的多個視訊串流。 在此情況下,某些串流預設狀態會是取消選取。 應用程式可以 呼叫 IMFPresentationDescriptor::SelectStream 和 IMFPresentationDescriptor::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;
}
此函式會執行下列步驟:
在數據流描述元上呼叫 IMFStreamDescriptor::GetMediaTypeHandler。 這個方法會傳回 IMFMediaTypeHandler 介面指標。
呼叫 IMFMediaTypeHandler:GetMajorType。 這個方法會傳回數據流的主要類型 GUID。
如果數據流類型為音訊,函式會呼叫 MFCreateAudioRendererActivate 來建立音訊轉譯器啟用物件。 如果數據流類型是視訊,函式會呼叫 MFCreateVideoRendererActivate 來建立視訊轉譯器啟用物件。 這兩個函式都會傳回 IMFActivate 介面的指標。 此指標用來初始化接收的輸出節點,如先前所示。
對於任何其他數據流類型,此範例會傳回錯誤碼。 或者,您可以簡單地取消選取串流。
後續步驟
若要一次播放一個媒體檔案,請將媒體會話上的拓撲排入佇列,方法是呼叫 IMFMediaSession::SetTopology。 媒體會話將使用拓撲載入器來解決拓撲問題。 如需完整的範例,請參閱 如何使用 Media Foundation 播放媒體檔案。
相關主題