Compartir a través de


Crear topologías de reproducción

En este tema se describe cómo crear una topología para la reproducción de audio o vídeo. Para la reproducción básica, puede crear una topología parcial, en la que los nodos de origen están conectados directamente a los nodos de salida. No es necesario insertar ningún nodo para las transformaciones intermedias, como descodificadores o convertidores de colores. La sesión multimedia usará el cargador de topologías para resolver la topología y el cargador de topología insertará las transformaciones necesarias.

Creación de la topología

Estos son los pasos generales para crear una topología de reproducción parcial desde un origen multimedia:

  1. Cree el origen multimedia. En la mayoría de los casos, usará la resolución de origen para crear el origen multimedia. Para obtener más información, consulte Resolución de origen.
  2. Obtenga el descriptor de presentación del origen multimedia.
  3. Cree una topología vacía.
  4. Use el descriptor de presentación para enumerar los descriptores de secuencia. Para cada descriptor de secuencia:
    1. Obtenga el tipo de medio principal de la secuencia, como audio o vídeo.
    2. Compruebe si la secuencia está seleccionada actualmente. (Opcionalmente, puede seleccionar o anular la selección de una secuencia, en función del tipo de medio).
    3. Si se selecciona la secuencia, cree un objeto de activación para el receptor de medios, en función del tipo de medio de la secuencia.
    4. Agregue un nodo de origen para la secuencia y un nodo de salida para el receptor de medios.
    5. Conecte el nodo de origen al nodo de salida.

Para facilitar el seguimiento de este proceso, el código de ejemplo de este tema se organiza en varias funciones. La función de nivel superior se denomina CreatePlaybackTopology. Toma tres parámetros:

  • Puntero a una interfaz IMFMediaSource de la fuente de medios.
  • Puntero a la interfaz IMFPresentationDescriptor del descriptor de presentación. Para obtener este puntero, llame a IMFMediaSource::CreatePresentationDescriptor. En el caso de los orígenes con varias presentaciones, los descriptores de presentación para las presentaciones posteriores se entregan en el evento MENewPresentation .
  • Identificador de una ventana de aplicación. Si el origen tiene una secuencia de vídeo, el vídeo se mostrará en esta ventana.

La función devuelve un puntero a una topología de reproducción parcial en el 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 función lleva a cabo los pasos siguientes:

  1. Llame a MFCreateTopology para crear la topología. Inicialmente, la topología no contiene ningún nodo.
  2. Llame a IMFPresentationDescriptor::GetStreamDescriptorCount para obtener el número de secuencias de la presentación.
  3. Para cada secuencia, llame a la función definida por AddBranchToPartialTopology la aplicación a una rama de la topología. Esta función se muestra en la sección siguiente.

Conexión de secuencias a receptores multimedia

Para cada secuencia seleccionada, agregue un nodo de origen y un nodo de salida y, a continuación, conecte los dos nodos. El nodo de origen representa la secuencia. El nodo de salida representa el representador de vídeo mejorado (EVR) o el representador de audio de streaming (SAR).

La AddBranchToPartialTopology función, que se muestra en el ejemplo siguiente, toma los parámetros siguientes:

  • Puntero a la interfaz IMFTopology de la topología.
  • Puntero a la interfaz IMFMediaSource de la fuente de medios.
  • Puntero a la interfaz IMFPresentationDescriptor del descriptor de presentación.
  • Índice de base cero de la secuencia.
  • Identificador de la ventana de vídeo. Este identificador solo se usa para la secuencia 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;
}

La función hace lo siguiente:

  1. Llama a IMFPresentationDescriptor::GetStreamDescriptorByIndex y pasa el índice de secuencia. Este método devuelve un puntero al descriptor de secuencia de esa secuencia, junto con un valor booleano que indica si la secuencia está seleccionada.
  2. Si la secuencia no está seleccionada, la función sale y devuelve S_OK, ya que la aplicación no necesita crear una rama de topología para una secuencia a menos que esté seleccionada.
  3. Si se selecciona la secuencia, la función completa la rama de topología de la siguiente manera:
    1. Crea un objeto de activación para el receptor mediante una llamada a la función CreateMediaSinkActivate definida por la aplicación. Esta función se muestra en la sección siguiente.
    2. Agrega un nodo de origen a la topología. El código de este paso se muestra en el tema Creación de nodos de origen.
    3. Agrega un nodo de salida a la topología. El código de este paso se muestra en el tema Creación de nodos de salida.
    4. Conecta los dos nodos llamando a IMFTopologyNode::ConnectOutput en el nodo de origen. Al conectar los nodos, la aplicación indica que el nodo ascendente debe entregar datos al nodo de bajada. Un nodo de origen tiene una salida y un nodo de salida tiene una entrada, por lo que ambos índices de flujo son cero.

Las aplicaciones más avanzadas pueden seleccionar o anular la selección de secuencias, en lugar de usar la configuración predeterminada del origen. Un origen podría tener varias secuencias y cualquiera de ellas se puede seleccionar de forma predeterminada. El descriptor de presentación del origen multimedia tiene un conjunto predeterminado de selecciones de secuencias. En un archivo de vídeo simple con una sola secuencia de audio y secuencia de vídeo, el origen multimedia normalmente seleccionará ambas secuencias de forma predeterminada. Sin embargo, un archivo puede tener varias secuencias de audio para distintos idiomas o varias secuencias de vídeo codificadas a diferentes velocidades de bits. En ese caso, algunas de las secuencias no se seleccionarán de forma predeterminada. La aplicación puede cambiar la selección llamando a IMFPresentationDescriptor::SelectStream y IMFPresentationDescriptor::D eselectStream en el descriptor de presentación.

Creación del receptor de medios

La siguiente función crea un objeto de activación para el receptor de medios 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;
}

Esta función lleva a cabo los pasos siguientes:

  1. Llama a IMFStreamDescriptor::GetMediaTypeHandler en el descriptor de secuencia. Este método devuelve un puntero de interfaz IMFMediaTypeHandler .

  2. Llama a IMFMediaTypeHandler::GetMajorType. Este método devuelve el GUID de tipo principal para la secuencia.

  3. Si el tipo de secuencia es audio, la función llama a MFCreateAudioRendererActivate para crear el objeto de activación del representador de audio. Si el tipo de secuencia es vídeo, la función llama a MFCreateVideoRendererActivate para crear el objeto de activación del representador de vídeo. Ambas funciones devuelven un puntero a la interfaz IMFActivate . Este puntero se usa para inicializar el nodo de salida del receptor, como se muestra anteriormente.

Para cualquier otro tipo de secuencia, en este ejemplo se devuelve un código de error. Como alternativa, simplemente podría anular la selección de la secuencia.

Pasos siguientes

Para reproducir un archivo multimedia a la vez, pone en cola la topología en la sesión multimedia llamando a IMFMediaSession::SetTopology. La sesión multimedia usará el cargador de topologías para resolver la topología. Para obtener un ejemplo completo, vea Cómo reproducir archivos multimedia con Media Foundation.

Cómo reproducir archivos multimedia no protegidos

Sesión multimedia

Topologías