Condividi tramite


Creazione di topologie di riproduzione

Questo argomento descrive come creare una topologia per la riproduzione audio o video. Per la riproduzione di base, è possibile creare una topologia parziale, in cui i nodi di origine sono connessi direttamente ai nodi di output. Non è necessario inserire nodi per le trasformazioni intermedie, ad esempio decodificatori o convertitori di colori. La sessione multimediale userà il caricatore della topologia per risolvere la topologia e il caricatore della topologia inserisce le trasformazioni necessarie.

Creazione della topologia

Ecco i passaggi generali per la creazione di una topologia di riproduzione parziale da un'origine multimediale:

  1. Creare l'origine multimediale. Nella maggior parte dei casi si userà il resolver di origine per creare l'origine multimediale. Per altre informazioni, vedere Resolver di origine.
  2. Ottenere il descrittore di presentazione dell'origine multimediale.
  3. Creare una topologia vuota.
  4. Usare il descrittore di presentazione per enumerare i descrittori di flusso. Per ogni descrittore di flusso:
    1. Ottenere il tipo di supporto principale del flusso, ad esempio audio o video.
    2. Controllare se il flusso è attualmente selezionato. Facoltativamente, è possibile selezionare o deselezionare un flusso in base al tipo di supporto.
    3. Se il flusso è selezionato, creare un oggetto di attivazione per il sink multimediale, in base al tipo di supporto del flusso.
    4. Aggiungere un nodo di origine per il flusso e un nodo di output per il sink multimediale.
    5. Connettere il nodo di origine al nodo di output.

Per semplificare questo processo, il codice di esempio in questo argomento è organizzato in diverse funzioni. La funzione di primo livello è denominata CreatePlaybackTopology. Accetta tre parametri:

La funzione restituisce un puntatore a una topologia di riproduzione parziale nel parametro 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;
}

Questa funzione esegue questa procedura:

  1. Chiamare MFCreateTo topologia per creare la topologia. Inizialmente, la topologia non contiene nodi.
  2. Chiamare IMFPresentationDescriptor::GetStreamDescriptorCount per ottenere il numero di flussi nella presentazione.
  3. Per ogni flusso, chiamare la funzione definita AddBranchToPartialTopology dall'applicazione in un ramo nella topologia. Questa funzione viene visualizzata nella sezione successiva.

Connessione dei flussi ai sink multimediali

Per ogni flusso selezionato, aggiungere un nodo di origine e un nodo di output, quindi connettere i due nodi. Il nodo di origine rappresenta il flusso. Il nodo di output rappresenta il renderer video avanzato (EVR) o il renderer audio di streaming (SAR).

La AddBranchToPartialTopology funzione, illustrata nell'esempio successivo, accetta i parametri seguenti:

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

La funzione esegue le operazioni seguenti:

  1. Chiama FMPresentationDescriptor::GetStreamDescriptorByIndex e passa l'indice di flusso. Questo metodo restituisce un puntatore al descrittore di flusso per tale flusso, insieme a un valore booleano che indica se il flusso è selezionato.
  2. Se il flusso non è selezionato, la funzione viene chiusa e restituisce S_OK, perché l'applicazione non deve creare un ramo di topologia per un flusso a meno che non sia selezionato.
  3. Se il flusso è selezionato, la funzione completa il ramo della topologia come indicato di seguito:
    1. Crea un oggetto di attivazione per il sink chiamando la funzione CreateMediaSinkActivate definita dall'applicazione. Questa funzione viene visualizzata nella sezione successiva.
    2. Aggiunge un nodo di origine alla topologia. Il codice per questo passaggio viene visualizzato nell'argomento Creazione di nodi di origine.
    3. Aggiunge un nodo di output alla topologia. Il codice per questo passaggio viene visualizzato nell'argomento Creazione di nodi di output.
    4. Connette i due nodi chiamando IMFTopologyNode::ConnectOutput nel nodo di origine. Connettendo i nodi, l'applicazione indica che il nodo upstream deve recapitare i dati al nodo downstream. Un nodo di origine ha un output e un nodo di output ha un input, quindi entrambi gli indici di flusso sono zero.

Altre applicazioni avanzate possono selezionare o deselezionare i flussi anziché usare la configurazione predefinita dell'origine. Un'origine potrebbe avere più flussi e una qualsiasi di esse potrebbe essere selezionata per impostazione predefinita. Il descrittore di presentazione dell'origine multimediale ha un set predefinito di selezioni di flusso. In un semplice file video con un singolo flusso audio e flusso video, l'origine multimediale seleziona in genere entrambi i flussi per impostazione predefinita. Tuttavia, un file potrebbe avere più flussi audio per lingue diverse o più flussi video codificati a velocità di bit diverse. In questo caso, alcuni flussi verranno deselezionati per impostazione predefinita. L'applicazione può modificare la selezione chiamando FMPresentationDescriptor::SelectStream e FMPresentationDescriptor::D eselectStream nel descrittore di presentazione.

Creazione del sink multimediale

La funzione successiva crea un oggetto di attivazione per il sink multimediale EVR o 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;
}

Questa funzione esegue questa procedura:

  1. Chiama FMStreamDescriptor::GetMediaTypeHandler nel descrittore di flusso. Questo metodo restituisce un puntatore dell'interfaccia FMMediaTypeHandler .

  2. Chiama IMFMediaTypeHandler::GetMajorType. Questo metodo restituisce il GUID di tipo principale per il flusso.

  3. Se il tipo di flusso è audio, la funzione chiama MFCreateAudioRendererActivate per creare l'oggetto di attivazione del renderer audio. Se il tipo di flusso è video, la funzione chiama MFCreateVideoRendererActivate per creare l'oggetto attivazione del renderer video. Entrambe queste funzioni restituiscono un puntatore all'interfaccia FMActivate . Questo puntatore viene usato per inizializzare il nodo di output per il sink, come illustrato in precedenza.

Per qualsiasi altro tipo di flusso, questo esempio restituisce un codice di errore. In alternativa, è possibile deselezionare semplicemente il flusso.

Passaggi successivi

Per riprodurre un file multimediale alla volta, accodare la topologia nella sessione multimediale chiamando FMMediaSession::SetTopology. La sessione multimediale userà il caricatore della topologia per risolvere la topologia. Per un esempio completo, vedere Come riprodurre file multimediali con Media Foundation.

Come riprodurre file multimediali non protetti

Sessione multimediale

Topologie