Compartilhar via


Criando topologias de reprodução

Este tópico descreve como criar uma topologia para reprodução de áudio ou vídeo. Para reprodução básica, você pode criar uma topologia parcial, na qual os nós de origem são conectados diretamente aos nós de saída. Você não precisa inserir nenhum nó para as transformações intermediárias, como decodificadores ou conversores de cores. A Sessão de Mídia usará o carregador de topologia para resolve a topologia e o carregador de topologia inserirá as transformações necessárias.

Criando a topologia

Estas são as etapas gerais para criar uma topologia de reprodução parcial de uma fonte de mídia:

  1. Crie a fonte de mídia. Na maioria dos casos, você usará o resolvedor de origem para criar a fonte de mídia. Para obter mais informações, consulte Resolvedor de origem.
  2. Obtenha o descritor de apresentação da fonte de mídia.
  3. Crie uma topologia vazia.
  4. Use o descritor de apresentação para enumerar os descritores de fluxo. Para cada descritor de fluxo:
    1. Obtenha o tipo de mídia principal do fluxo, como áudio ou vídeo.
    2. Verifique se o fluxo está selecionado no momento. (Opcionalmente, você pode selecionar ou desmarcar um fluxo, com base no tipo de mídia.)
    3. Se o fluxo estiver selecionado, crie um objeto de ativação para o coletor de mídia, com base no tipo de mídia do fluxo.
    4. Adicione um nó de origem para o fluxo e um nó de saída para o coletor de mídia.
    5. Conecte o nó de origem ao nó de saída.

Para facilitar o acompanhamento desse processo, o código de exemplo neste tópico é organizado em várias funções. A função de nível superior é chamada CreatePlaybackTopology. Ele usa três parâmetros:

  • Um ponteiro para uma interface IMFMediaSource da fonte de mídia.
  • Um ponteiro para a interface IMFPresentationDescriptor do descritor de apresentação. Obtenha esse ponteiro chamando IMFMediaSource::CreatePresentationDescriptor. Para fontes com várias apresentações, os descritores de apresentação para apresentações subsequentes são entregues no evento MENewPresentation .
  • Um identificador para uma janela do aplicativo. Se a origem tiver um fluxo de vídeo, o vídeo será exibido nesta janela.

A função retorna um ponteiro para uma topologia de reprodução parcial no parâmetro 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;
}

Esta função realiza as seguintes etapas:

  1. Chame MFCreateTopology para criar a topologia. Inicialmente, a topologia não contém nenhum nó.
  2. Chame IMFPresentationDescriptor::GetStreamDescriptorCount para obter o número de fluxos na apresentação.
  3. Para cada fluxo, chame a função definida AddBranchToPartialTopology pelo aplicativo para um branch na topologia. Essa função é mostrada na próxima seção.

Conectando fluxos a coletores de mídia

Para cada fluxo selecionado, adicione um nó de origem e um nó de saída e conecte os dois nós. O nó de origem representa o fluxo. O nó de saída representa o EVR ( Renderizador de Vídeo Avançado ) ou o SAR ( Renderizador de Áudio de Streaming ).

A AddBranchToPartialTopology função, mostrada no próximo exemplo, usa os seguintes parâmetros:

  • Um ponteiro para a interface IMFTopology da topologia.
  • Um ponteiro para a interface IMFMediaSource da fonte de mídia.
  • Um ponteiro para a interface IMFPresentationDescriptor do descritor de apresentação.
  • O índice baseado em zero do fluxo.
  • Um identificador para a janela de vídeo. Esse identificador é usado apenas para o fluxo de vídeo.
//  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;
}

A função faz o seguinte:

  1. Chama IMFPresentationDescriptor::GetStreamDescriptorByIndex e passa no índice de fluxo. Esse método retorna um ponteiro para o descritor de fluxo para esse fluxo, juntamente com um valor booliano que indica se o fluxo está selecionado.
  2. Se o fluxo não estiver selecionado, a função sairá e retornará S_OK, pois o aplicativo não precisará criar um branch de topologia para um fluxo, a menos que esteja selecionado.
  3. Se o fluxo estiver selecionado, a função concluirá o branch de topologia da seguinte maneira:
    1. Cria um objeto de ativação para o coletor chamando a função CreateMediaSinkActivate definida pelo aplicativo. Essa função é mostrada na próxima seção.
    2. Adiciona um nó de origem à topologia. O código para esta etapa é mostrado no tópico Criando nós de origem.
    3. Adiciona um nó de saída à topologia. O código para esta etapa é mostrado no tópico Criando nós de saída.
    4. Conecta os dois nós chamando IMFTopologyNode::ConnectOutput no nó de origem. Ao conectar os nós, o aplicativo indica que o nó upstream deve fornecer dados para o nó downstream. Um nó de origem tem uma saída e um nó de saída tem uma entrada, portanto, ambos os índices de fluxo são zero.

Aplicativos mais avançados podem selecionar ou desmarcar fluxos, em vez de usar a configuração padrão da origem. Uma fonte pode ter vários fluxos e qualquer um deles pode ser selecionado por padrão. O descritor de apresentação da fonte de mídia tem um conjunto padrão de seleções de fluxo. Em um arquivo de vídeo simples com um único fluxo de áudio e fluxo de vídeo, a fonte de mídia geralmente selecionará ambos os fluxos por padrão. No entanto, um arquivo pode ter vários fluxos de áudio para idiomas diferentes ou vários fluxos de vídeo codificados a taxas de bits diferentes. Nesse caso, alguns dos fluxos serão desmarcados por padrão. O aplicativo pode alterar a seleção chamando IMFPresentationDescriptor::SelectStream e IMFPresentationDescriptor::D eselectStream no descritor de apresentação.

Criando o coletor de mídia

A próxima função cria um objeto de ativação para o coletor de mídia EVR ou 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;
}

Esta função realiza as seguintes etapas:

  1. Chama IMFStreamDescriptor::GetMediaTypeHandler no descritor de fluxo. Esse método retorna um ponteiro de interface IMFMediaTypeHandler .

  2. Chama IMFMediaTypeHandler::GetMajorType. Esse método retorna o GUID do tipo principal para o fluxo.

  3. Se o tipo de fluxo for áudio, a função chamará MFCreateAudioRendererActivate para criar o objeto de ativação do renderizador de áudio. Se o tipo de fluxo for vídeo, a função chamará MFCreateVideoRendererActivate para criar o objeto de ativação do renderizador de vídeo. Ambas as funções retornam um ponteiro para a interface IMFActivate . Esse ponteiro é usado para inicializar o nó de saída do coletor, conforme mostrado anteriormente.

Para qualquer outro tipo de fluxo, este exemplo retorna um código de erro. Como alternativa, você pode simplesmente desmarcar o fluxo.

Próximas etapas

Para reproduzir um arquivo de mídia por vez, enfileira a topologia na Sessão de Mídia chamando IMFMediaSession::SetTopology. A Sessão de Mídia usará o carregador de topologia para resolve a topologia. Para obter um exemplo completo, consulte Como reproduzir arquivos de mídia com o Media Foundation.

Como reproduzir arquivos de mídia desprotegidos

Sessão de Mídia

Topologias