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:
- Cria um novo grafo de filtro (vazio).
- Chama IGraphBuilder::AddSourceFilter para adicionar um filtro de origem para o arquivo especificado.
- 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:
- Chama CoCreateInstance para criar uma nova instância do Gerenciador de Grafo de Filtro.
- Consulta o Gerenciador de Grafo de Filtro para as interfaces IMediaControl e IMediaEventEx .
- 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.
- Consulta o Gerenciador de Grafo de Filtro para a interface IFilterGraph2 .
- Adiciona um filtro de renderizador de vídeo ao grafo de filtro.
- 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.
- Enumera os pinos de saída no filtro de origem. Para obter mais informações sobre como enumerar pinos, consulte Enumerando pinos.
- 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).
- Chama
CVideoRenderer::FinalizeGraph
para concluir a inicialização do renderizador de vídeo. - 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.
Tópicos relacionados