Partilhar via


Criação de 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. Não precisas de inserir nós para as transformações intermediárias, como decodificadores ou conversores de cores. A Sessão de Mídia usará o carregador de topologia para resolver a topologia e o carregador de topologia inserirá as transformações necessárias.

Criando a topologia

Aqui estão as etapas gerais para criar uma topologia de reprodução parcial a partir 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 Source Resolver.
  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 principal tipo de mídia do stream, 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 tornar esse processo mais fácil de seguir, o código de exemplo neste tópico é organizado em várias funções. A função de nível superior é denominada CreatePlaybackTopology. São necessários três parâmetros:

  • Um ponteiro para a 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 múltiplas apresentações, os descritores de apresentação para apresentações subsequentes são fornecidos no evento MENewPresentation.
  • Um identificador para uma janela do aplicativo. Se a fonte 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 executa 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 AddBranchToPartialTopology definida pelo aplicativo para uma ramificação na topologia. Esta 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, em seguida, conecte os dois nós. O nó de origem representa o fluxo. O nó de saída representa o Enhanced Video Renderer (EVR) ou o Streaming Audio Renderer (SAR).

A função AddBranchToPartialTopology, mostrada no exemplo a seguir, 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.
  • Uma alça para a janela de vídeo. Este 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 fornece o índice do fluxo. Esse método retorna um ponteiro para o descritor de fluxo para esse fluxo, juntamente com um valor booleano indicando se o fluxo está selecionado.
  2. Se o fluxo não for selecionado, a função será encerrada e retornará S_OK, porque o aplicativo não precisa criar uma ramificação de topologia para um fluxo, a menos que ele seja selecionado.
  3. Se o fluxo for selecionado, a função concluirá a ramificação da topologia da seguinte maneira:
    1. Cria um objeto de ativação para o coletor, chamando a função CreateMediaSinkActivate definida pelo aplicativo. Esta 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 ao chamar IMFTopologyNode::ConnectOutput no nó de origem. Ao conectar os nós, a aplicação indica que o nó montante deve entregar dados ao nó jusante. 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 fonte. 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 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 em taxas de bits diferentes. Nesse caso, alguns dos fluxos estarão não selecionados por padrão. A aplicação pode alterar a seleção chamando IMFPresentationDescriptor::SelectStream e IMFPresentationDescriptor::DeselectStream 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 executa as seguintes etapas:

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

  2. Chama IMFMediaTypeHandler::GetMajorType. Esse método retorna o GUID de 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, pode simplesmente desmarcar a transmissão.

Próximos passos

Para reproduzir um arquivo de mídia de cada vez, enfileire a topologia na Sessão de Mídia chamando IMFMediaSession::SetTopology. A Sessão de Multimédia usará o carregador de topologia para resolver 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 Imprensa

Topologias