Compartir a través de


Paso 3: Compilar el gráfico de filtros

[La característica asociada a esta página, DirectShow, es una característica heredada. Se ha reemplazado por MediaPlayer, IMFMediaEngine y Captura de audio/vídeo en Media Foundation. Esas características se han optimizado para Windows 10 y Windows 11. Microsoft recomienda encarecidamente que el nuevo código use MediaPlayer, IMFMediaEngine y Audio/Video Capture en Media Foundation en lugar de DirectShow, siempre que sea posible. Microsoft sugiere que el código existente que usa las API heredadas se reescriba para usar las nuevas API si es posible.

Este tema es el paso 3 del tutorial Reproducción de audio y vídeo en DirectShow. El código completo se muestra en el tema Ejemplo de reproducción de DirectShow.

El siguiente paso es crear un grafo de filtro para reproducir el archivo multimedia.

Abrir un archivo multimedia

El DShowPlayer::OpenFile método abre un archivo multimedia para la reproducción. Este método hace lo siguiente:

  1. Crea un nuevo gráfico de filtros (vacío).
  2. Llama a IGraphBuilder::AddSourceFilter para agregar un filtro de origen para el archivo especificado.
  3. Representa las secuencias en el filtro de origen.
HRESULT DShowPlayer::OpenFile(PCWSTR pszFileName)
{
    IBaseFilter *pSource = NULL;

    // Create a new filter graph. (This also closes the old one, if any.)
    HRESULT hr = InitializeGraph();
    if (FAILED(hr))
    {
        goto done;
    }
    
    // Add the source filter to the graph.
    hr = m_pGraph->AddSourceFilter(pszFileName, NULL, &pSource);
    if (FAILED(hr))
    {
        goto done;
    }

    // Try to render the streams.
    hr = RenderStreams(pSource);

done:
    if (FAILED(hr))
    {
        TearDownGraph();
    }
    SafeRelease(&pSource);
    return hr;
}

Creación del Administrador de gráficos de filtros

El DShowPlayer::InitializeGraph método crea un nuevo gráfico de filtros. Este método hace lo siguiente:

  1. Llama a CoCreateInstance para crear una nueva instancia del Administrador de gráficos de filtros.
  2. Consulta el Administrador de gráficos de filtros para las interfaces IMediaControl e IMediaEventEx .
  3. Llama a IMediaEventEx::SetNotifyWindow para configurar la notificación de eventos. Para obtener más información, vea Notificación de eventos en DirectShow.
HRESULT DShowPlayer::InitializeGraph()
{
    TearDownGraph();

    // Create the Filter Graph Manager.
    HRESULT hr = CoCreateInstance(CLSID_FilterGraph, NULL, 
        CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&m_pGraph));
    if (FAILED(hr))
    {
        goto done;
    }

    hr = m_pGraph->QueryInterface(IID_PPV_ARGS(&m_pControl));
    if (FAILED(hr))
    {
        goto done;
    }

    hr = m_pGraph->QueryInterface(IID_PPV_ARGS(&m_pEvent));
    if (FAILED(hr))
    {
        goto done;
    }

    // Set up event notification.
    hr = m_pEvent->SetNotifyWindow((OAHWND)m_hwnd, WM_GRAPH_EVENT, NULL);
    if (FAILED(hr))
    {
        goto done;
    }

    m_state = STATE_STOPPED;

done:
    return hr;
}

Representación de las secuencias

El siguiente paso consiste en conectar el filtro de origen a uno o varios filtros de representador.

El DShowPlayer::RenderStreams método realiza los pasos siguientes.

  1. Consulta el Administrador de gráficos de filtros para la interfaz IFilterGraph2 .
  2. Agrega un filtro de representador de vídeo al gráfico de filtros.
  3. Agrega el filtro directSound Renderer al gráfico de filtros para admitir la reproducción de audio. Para obtener más información sobre cómo agregar filtros al gráfico de filtros, vea Agregar un filtro por CLSID.
  4. Enumera las patillas de salida en el filtro de origen. Para obtener más información sobre la enumeración de patillas, vea Enumerar pines.
  5. Para cada pin, llama al método IFilterGraph2::RenderEx . Este método conecta el pin de salida a un filtro de representador, agregando filtros intermedios si es necesario (por ejemplo, descodificadores).
  6. Llama CVideoRenderer::FinalizeGraph a para terminar de inicializar el representador de vídeo.
  7. Quita el filtro directSound Renderer si ese filtro no está conectado a otro filtro. Esto puede ocurrir si el archivo de origen no contiene una secuencia de audio.
// Render the streams from a source filter. 

HRESULT DShowPlayer::RenderStreams(IBaseFilter *pSource)
{
    BOOL bRenderedAnyPin = FALSE;

    IFilterGraph2 *pGraph2 = NULL;
    IEnumPins *pEnum = NULL;
    IBaseFilter *pAudioRenderer = NULL;
    HRESULT hr = m_pGraph->QueryInterface(IID_PPV_ARGS(&pGraph2));
    if (FAILED(hr))
    {
        goto done;
    }

    // Add the video renderer to the graph
    hr = CreateVideoRenderer();
    if (FAILED(hr))
    {
        goto done;
    }

    // Add the DSound Renderer to the graph.
    hr = AddFilterByCLSID(m_pGraph, CLSID_DSoundRender, 
        &pAudioRenderer, L"Audio Renderer");
    if (FAILED(hr))
    {
        goto done;
    }

    // Enumerate the pins on the source filter.
    hr = pSource->EnumPins(&pEnum);
    if (FAILED(hr))
    {
        goto done;
    }

    // Loop through all the pins
    IPin *pPin;
    while (S_OK == pEnum->Next(1, &pPin, NULL))
    {           
        // Try to render this pin. 
        // It's OK if we fail some pins, if at least one pin renders.
        HRESULT hr2 = pGraph2->RenderEx(pPin, AM_RENDEREX_RENDERTOEXISTINGRENDERERS, NULL);

        pPin->Release();
        if (SUCCEEDED(hr2))
        {
            bRenderedAnyPin = TRUE;
        }
    }

    hr = m_pVideo->FinalizeGraph(m_pGraph);
    if (FAILED(hr))
    {
        goto done;
    }

    // Remove the audio renderer, if not used.
    BOOL bRemoved;
    hr = RemoveUnconnectedRenderer(m_pGraph, pAudioRenderer, &bRemoved);

done:
    SafeRelease(&pEnum);
    SafeRelease(&pAudioRenderer);
    SafeRelease(&pGraph2);

    // If we succeeded to this point, make sure we rendered at least one 
    // stream.
    if (SUCCEEDED(hr))
    {
        if (!bRenderedAnyPin)
        {
            hr = VFW_E_CANNOT_RENDER;
        }
    }
    return hr;
}

Este es el código de la RemoveUnconnectedRenderer función , que se usa en el ejemplo anterior.

HRESULT RemoveUnconnectedRenderer(IGraphBuilder *pGraph, IBaseFilter *pRenderer, BOOL *pbRemoved)
{
    IPin *pPin = NULL;

    *pbRemoved = FALSE;

    // Look for a connected input pin on the renderer.

    HRESULT hr = FindConnectedPin(pRenderer, PINDIR_INPUT, &pPin);
    SafeRelease(&pPin);

    // If this function succeeds, the renderer is connected, so we don't remove it.
    // If it fails, it means the renderer is not connected to anything, so
    // we remove it.

    if (FAILED(hr))
    {
        hr = pGraph->RemoveFilter(pRenderer);
        *pbRemoved = TRUE;
    }

    return hr;
}

Liberación del gráfico de filtros

Cuando se cierra la aplicación, debe liberar el gráfico de filtros, como se muestra en el código siguiente.

void DShowPlayer::TearDownGraph()
{
    // Stop sending event messages
    if (m_pEvent)
    {
        m_pEvent->SetNotifyWindow((OAHWND)NULL, NULL, NULL);
    }

    SafeRelease(&m_pGraph);
    SafeRelease(&m_pControl);
    SafeRelease(&m_pEvent);

    delete m_pVideo;
    m_pVideo = NULL;

    m_state = STATE_NO_GRAPH;
}

Siguiente: Paso 4: Agregar el representador de vídeo.

Reproducción de audio y vídeo en DirectShow

Ejemplo de reproducción de DirectShow

Creación del gráfico de filtros

Técnicas generales de Graph-Building