Erstellen von Wiedergabetopologien

In diesem Thema wird beschrieben, wie Sie eine Topologie für die Audio- oder Videowiedergabe erstellen. Für die einfache Wiedergabe können Sie eine partielle Topologie erstellen, in der die Quellknoten direkt mit den Ausgabeknoten verbunden sind. Sie müssen keine Knoten für die Zwischentransformationen einfügen, z. B. Decoder oder Farbkonverter. Die Mediensitzung verwendet den Topologieladeprogramm, um die Topologie aufzulösen, und das Topologieladeprogramm fügt die erforderlichen Transformationen ein.

Erstellen der Topologie

Hier sind die allgemeinen Schritte zum Erstellen einer partiellen Wiedergabetopologie aus einer Medienquelle:

  1. Erstellen Sie die Medienquelle. In den meisten Fällen verwenden Sie den Quelllöser, um die Medienquelle zu erstellen. Weitere Informationen finden Sie unter Quellre resolver.
  2. Rufen Sie den Präsentationsdeskriptor der Medienquelle ab.
  3. Erstellen Sie eine leere Topologie.
  4. Verwenden Sie den Präsentationsdeskriptor, um die Streamdeskriptoren aufzulisten. Für jeden Streamdeskriptor:
    1. Rufen Sie den Hauptmedientyp des Streams ab, z. B. Audio oder Video.
    2. Überprüfen Sie, ob der Stream derzeit ausgewählt ist. (Optional können Sie einen Stream basierend auf dem Medientyp auswählen oder deaktivieren.)
    3. Wenn der Stream ausgewählt ist, erstellen Sie basierend auf dem Medientyp des Datenstroms ein Aktivierungsobjekt für die Mediensenke.
    4. Fügen Sie einen Quellknoten für den Stream und einen Ausgabeknoten für die Mediensenke hinzu.
    5. Verbinden Sie den Quellknoten mit dem Ausgabeknoten.

Um diesen Prozess zu vereinfachen, ist der Beispielcode in diesem Thema in mehrere Funktionen unterteilt. Die Funktion der obersten Ebene heißt CreatePlaybackTopology. Es werden drei Parameter benötigt:

Die Funktion gibt einen Zeiger auf eine partielle Wiedergabetopologie im ppTopology-Parameter zurück.

//  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;
}

Diese Funktion führt folgende Schritte aus:

  1. Rufen Sie MFCreateTopology auf, um die Topologie zu erstellen. Anfänglich enthält die Topologie keine Knoten.
  2. Rufen Sie IMFPresentationDescriptor::GetStreamDescriptorCount auf, um die Anzahl der Streams in der Präsentation abzurufen.
  3. Rufen Sie für jeden Stream die anwendungsdefinierte AddBranchToPartialTopology Funktion in einen Branch in der Topologie auf. Diese Funktion wird im nächsten Abschnitt angezeigt.

Verbinden von Streams mit Mediensenken

Fügen Sie für jeden ausgewählten Stream einen Quellknoten und einen Ausgabeknoten hinzu, und verbinden Sie dann die beiden Knoten. Der Quellknoten stellt den Stream dar. Der Ausgabeknoten stellt entweder den Enhanced Video Renderer (EVR) oder den Streaming Audio Renderer (SAR) dar.

Die AddBranchToPartialTopology im nächsten Beispiel gezeigte Funktion verwendet die folgenden Parameter:

//  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;
}

Die Funktion führt Folgendes aus:

  1. Ruft IMFPresentationDescriptor::GetStreamDescriptorByIndex auf und übergibt den Streamindex. Diese Methode gibt einen Zeiger auf den Streamdeskriptor für diesen Stream zusammen mit einem booleschen Wert zurück, der angibt, ob der Stream ausgewählt ist.
  2. Wenn der Stream nicht ausgewählt ist, wird die Funktion beendet und gibt S_OK zurück, da die Anwendung keinen Topologiebranch für einen Stream erstellen muss, es sei denn, er ist ausgewählt.
  3. Wenn der Stream ausgewählt ist, schließt die Funktion den Topologiebranch wie folgt ab:
    1. Erstellt ein Aktivierungsobjekt für die Senke, indem die anwendungsdefinierte CreateMediaSinkActivate-Funktion aufgerufen wird. Diese Funktion wird im nächsten Abschnitt angezeigt.
    2. Fügt der Topologie einen Quellknoten hinzu. Der Code für diesen Schritt wird im Thema Erstellen von Quellknoten angezeigt.
    3. Fügt der Topologie einen Ausgabeknoten hinzu. Der Code für diesen Schritt wird im Thema Erstellen von Ausgabeknoten angezeigt.
    4. Verbindet die beiden Knoten durch Aufrufen von IMFTopologyNode::ConnectOutput auf dem Quellknoten. Durch das Verbinden der Knoten gibt die Anwendung an, dass der Upstream Knoten Daten an den Downstreamknoten übermitteln soll. Ein Quellknoten verfügt über eine Ausgabe und ein Ausgabeknoten über eine Eingabe, sodass beide Streamindizes null sind.

Erweiterte Anwendungen können Streams auswählen oder deaktivieren, anstatt die Standardkonfiguration der Quelle zu verwenden. Eine Quelle kann mehrere Streams enthalten, und jeder davon kann standardmäßig ausgewählt sein. Der Präsentationsdeskriptor der Medienquelle verfügt über einen Standardsatz von Streamauswahlen. In einer einfachen Videodatei mit einem einzelnen Audio- und Videostream wählt die Medienquelle in der Regel standardmäßig beide Streams aus. Eine Datei kann jedoch mehrere Audiostreams für verschiedene Sprachen oder mehrere Videostreams mit unterschiedlichen Bitraten codiert haben. In diesem Fall werden einige der Streams standardmäßig nicht ausgewählt. Die Anwendung kann die Auswahl ändern, indem im Präsentationsdeskriptor IMFPresentationDescriptor::SelectStream und IMFPresentationDescriptor::D eselectStream aufgerufen wird.

Erstellen der Mediensenke

Die nächste Funktion erstellt ein Aktivierungsobjekt für die EVR- oder SAR-Mediensenke.

//  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;
}

Diese Funktion führt folgende Schritte aus:

  1. Ruft IMFStreamDescriptor::GetMediaTypeHandler für den Streamdeskriptor auf. Diese Methode gibt einen IMFMediaTypeHandler-Schnittstellenzeiger zurück.

  2. Ruft IMFMediaTypeHandler::GetMajorType auf. Diese Methode gibt die Haupttyp-GUID für den Stream zurück.

  3. Wenn der Streamtyp Audio ist, ruft die Funktion MFCreateAudioRendererActivate auf, um das Aktivierungsobjekt des Audiorenderers zu erstellen. Wenn der Streamtyp Video ist, ruft die Funktion MFCreateVideoRendererActivate auf, um das Aktivierungsobjekt des Videorenderers zu erstellen. Beide Funktionen geben einen Zeiger auf die IMFActivate-Schnittstelle zurück . Dieser Zeiger wird verwendet, um den Ausgabeknoten für die Senke zu initialisieren, wie zuvor gezeigt.

Für jeden anderen Streamtyp gibt dieses Beispiel einen Fehlercode zurück. Alternativ können Sie die Auswahl des Streams einfach aufheben.

Nächste Schritte

Um jeweils eine Mediendatei wiederzugeben, stellen Sie die Topologie in der Mediensitzung in eine Warteschlange, indem Sie IMFMediaSession::SetTopology aufrufen. Die Mediensitzung verwendet den Topologieladeprogramm, um die Topologie aufzulösen. Ein vollständiges Beispiel finden Sie unter Wiedergeben von Mediendateien mit Media Foundation.

Wiedergeben von ungeschützten Mediendateien

Mediensitzung

Topologien