Compartilhar via


Etapa 3: Criar o grafo de filtro

[O recurso associado a esta página, DirectShow, é um recurso herdado. Foi substituído por MediaPlayer, IMFMediaEngine e Audio/Video Capture in Media Foundation. Esses recursos foram otimizados para Windows 10 e Windows 11. A Microsoft recomenda fortemente que o novo código use MediaPlayer, IMFMediaEngine e Audio/Video Capture in Media Foundation em vez de DirectShow, quando possível. A Microsoft sugere que o código existente que usa as APIs herdadas seja reescrito para usar as novas APIs, se possível.]

Este tópico é a etapa 3 do tutorial Reprodução de áudio/vídeo no DirectShow. O código completo é mostrado no tópico Exemplo de Reprodução do DirectShow.

A próxima etapa é criar um grafo de filtro para reproduzir o arquivo de mídia.

Abrindo um arquivo de mídia

O DShowPlayer::OpenFile método abre um arquivo de mídia para reprodução. Esse método faz o seguinte:

  1. Cria um novo grafo de filtro (vazio).
  2. Chama IGraphBuilder::AddSourceFilter para adicionar um filtro de origem para o arquivo especificado.
  3. Renderiza os fluxos no filtro de origem.
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;
}

Criando o Gerenciador de Grafo de Filtro

O DShowPlayer::InitializeGraph método cria um novo grafo de filtro. Esse método faz o seguinte:

  1. Chama CoCreateInstance para criar uma nova instância do Gerenciador de Grafo de Filtro.
  2. Consulta o Gerenciador de Grafo de Filtro para as interfaces IMediaControl e IMediaEventEx .
  3. Chama IMediaEventEx::SetNotifyWindow para configurar a notificação de evento. Para obter mais informações, consulte Notificação de eventos no 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;
}

Renderizando os fluxos

A próxima etapa é conectar o filtro de origem a um ou mais filtros de renderizador.

O DShowPlayer::RenderStreams método executa as etapas a seguir.

  1. Consulta o Gerenciador de Grafo de Filtro para a interface IFilterGraph2 .
  2. Adiciona um filtro de renderizador de vídeo ao grafo de filtro.
  3. Adiciona o Filtro do Renderizador DirectSound ao grafo de filtro para dar suporte à reprodução de áudio. Para obter mais informações sobre como adicionar filtros ao grafo de filtro, consulte Adicionar um filtro por CLSID.
  4. Enumera os pinos de saída no filtro de origem. Para obter mais informações sobre como enumerar pinos, consulte Enumerando pinos.
  5. Para cada pino, chama o método IFilterGraph2::RenderEx . Esse método conecta o pino de saída a um filtro de renderizador, adicionando filtros intermediários, se necessário (como decodificadores).
  6. Chama CVideoRenderer::FinalizeGraph para concluir a inicialização do renderizador de vídeo.
  7. Remove o filtro do Renderizador DirectSound se esse filtro não estiver conectado a outro filtro. Isso pode ocorrer se o arquivo de origem não contiver um fluxo de áudio.
// 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;
}

Aqui está o código para a RemoveUnconnectedRenderer função , que é usada no exemplo 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;
}

Liberando o grafo de filtro

Quando o aplicativo é encerrado, ele deve liberar o grafo de filtro, conforme mostrado no código a seguir.

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

Próximo: Etapa 4: Adicionar o Renderizador de Vídeo.

Reprodução de áudio/vídeo no DirectShow

Exemplo de reprodução do DirectShow

Criando o grafo de filtro

Técnicas gerais de Graph-Building