Etapa 4: Adicionar o Renderizador de Vídeo
[O recurso associado a esta página, DirectShow, é um recurso herdado. Ele foi substituído por MediaPlayer, IMFMediaEngine e Captura de Áudio/Vídeo na 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 Captura de Áudio/Vídeo no 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 4 do tutorial Reprodução de Áudio/Vídeo no DirectShow. O código completo é mostrado no tópico Exemplo de Reprodução do DirectShow.
O DirectShow fornece vários filtros diferentes que renderizam vídeo:
- EVR (Filtro avançado de renderizador de vídeo )
- Filtro do renderizador de combinação de vídeo 9 (VMR-9)
- Filtro do Renderizador de Combinação de Vídeo 7 (VMR-7)
Para obter mais informações sobre as diferenças entre esses filtros, consulte Escolhendo o renderizador de vídeo correto.
Neste tutorial, cada filtro de renderizador de vídeo é encapsulado por uma classe que abstrai algumas das diferenças entre eles. Todas essas classes derivam de uma classe base abstrata chamada CVideoRenderer
. A declaração de CVideoRenderer
é mostrada na Etapa 2: Declarar classes derivadas e CVideoRenderer.
O método a seguir tenta criar cada renderizador de vídeo, começando com o EVR, depois o VMR-9 e, por fim, a VMR-7.
HRESULT DShowPlayer::CreateVideoRenderer()
{
HRESULT hr = E_FAIL;
enum { Try_EVR, Try_VMR9, Try_VMR7 };
for (DWORD i = Try_EVR; i <= Try_VMR7; i++)
{
switch (i)
{
case Try_EVR:
m_pVideo = new (std::nothrow) CEVR();
break;
case Try_VMR9:
m_pVideo = new (std::nothrow) CVMR9();
break;
case Try_VMR7:
m_pVideo = new (std::nothrow) CVMR7();
break;
}
if (m_pVideo == NULL)
{
hr = E_OUTOFMEMORY;
break;
}
hr = m_pVideo->AddToGraph(m_pGraph, m_hwnd);
if (SUCCEEDED(hr))
{
break;
}
delete m_pVideo;
m_pVideo = NULL;
}
return hr;
}
Filtro EVR
O código a seguir cria o filtro EVR e o adiciona ao grafo de filtro. A função AddFilterByCLSID
usada neste exemplo é mostrada no tópico Adicionar um filtro por CLSID.
HRESULT CEVR::AddToGraph(IGraphBuilder *pGraph, HWND hwnd)
{
IBaseFilter *pEVR = NULL;
HRESULT hr = AddFilterByCLSID(pGraph, CLSID_EnhancedVideoRenderer,
&pEVR, L"EVR");
if (FAILED(hr))
{
goto done;
}
hr = InitializeEVR(pEVR, hwnd, &m_pVideoDisplay);
if (FAILED(hr))
{
goto done;
}
// Note: Because IMFVideoDisplayControl is a service interface,
// you cannot QI the pointer to get back the IBaseFilter pointer.
// Therefore, we need to cache the IBaseFilter pointer.
m_pEVR = pEVR;
m_pEVR->AddRef();
done:
SafeRelease(&pEVR);
return hr;
}
A InitializeEVR
função inicializa o filtro EVR. Essa função executa as etapas a seguir.
- Consulta o filtro para a interface IMFGetService .
- Chama IMFGetService::GetService para obter um ponteiro para a interface IMFVideoDisplayControl .
- Chama IMFVideoDisplayControl::SetVideoWindow para definir a janela de vídeo.
- Chama IMFVideoDisplayControl::SetAspectRatioMode para configurar o EVR para preservar a taxa de proporção de vídeo.
O código a seguir mostra a InitializeEVR
função .
HRESULT InitializeEVR(
IBaseFilter *pEVR, // Pointer to the EVR
HWND hwnd, // Clipping window
IMFVideoDisplayControl** ppDisplayControl
)
{
IMFGetService *pGS = NULL;
IMFVideoDisplayControl *pDisplay = NULL;
HRESULT hr = pEVR->QueryInterface(IID_PPV_ARGS(&pGS));
if (FAILED(hr))
{
goto done;
}
hr = pGS->GetService(MR_VIDEO_RENDER_SERVICE, IID_PPV_ARGS(&pDisplay));
if (FAILED(hr))
{
goto done;
}
// Set the clipping window.
hr = pDisplay->SetVideoWindow(hwnd);
if (FAILED(hr))
{
goto done;
}
// Preserve aspect ratio by letter-boxing
hr = pDisplay->SetAspectRatioMode(MFVideoARMode_PreservePicture);
if (FAILED(hr))
{
goto done;
}
// Return the IMFVideoDisplayControl pointer to the caller.
*ppDisplayControl = pDisplay;
(*ppDisplayControl)->AddRef();
done:
SafeRelease(&pGS);
SafeRelease(&pDisplay);
return hr;
}
Depois que o grafo é criado, o DShowPlayer::RenderStreams
chama CVideoRenderer::FinalizeGraph
. Esse método executa qualquer inicialização ou limpeza final. O código a seguir mostra a CEVR
implementação desse método.
HRESULT CEVR::FinalizeGraph(IGraphBuilder *pGraph)
{
if (m_pEVR == NULL)
{
return S_OK;
}
BOOL bRemoved;
HRESULT hr = RemoveUnconnectedRenderer(pGraph, m_pEVR, &bRemoved);
if (bRemoved)
{
SafeRelease(&m_pEVR);
SafeRelease(&m_pVideoDisplay);
}
return hr;
}
Se o EVR não estiver conectado a nenhum outro filtro, esse método removerá o EVR do grafo. Isso pode ocorrer se o arquivo de mídia não contiver um fluxo de vídeo.
Filtro VMR-9
O código a seguir cria o filtro VMR-9 e o adiciona ao grafo de filtro.
HRESULT CVMR9::AddToGraph(IGraphBuilder *pGraph, HWND hwnd)
{
IBaseFilter *pVMR = NULL;
HRESULT hr = AddFilterByCLSID(pGraph, CLSID_VideoMixingRenderer9,
&pVMR, L"VMR-9");
if (SUCCEEDED(hr))
{
// Set windowless mode on the VMR. This must be done before the VMR
// is connected.
hr = InitWindowlessVMR9(pVMR, hwnd, &m_pWindowless);
}
SafeRelease(&pVMR);
return hr;
}
A InitWindowlessVMR9
função inicializa a VMR-9 para o modo sem janelas. (Para obter mais informações sobre o modo sem janelas, consulte Modo sem janela da VMR.) Essa função executa as etapas a seguir.
- Consulta o filtro VMR-9 para a interface IVMRFilterConfig9 .
- Chama o método IVMRFilterConfig9::SetRenderingMode para definir o modo sem janelas.
- Consulta o filtro VMR-9 para a interface IVMRWindowlessControl9 .
- Chama o método IVMRWindowlessControl9::SetVideoClippingWindow para definir a janela de vídeo.
- Chama o método IVMRWindowlessControl9::SetAspectRatioMode para preservar a taxa de proporção de vídeo.
O código a seguir mostra a InitWindowlessVMR9
função .
HRESULT InitWindowlessVMR9(
IBaseFilter *pVMR, // Pointer to the VMR
HWND hwnd, // Clipping window
IVMRWindowlessControl9** ppWC // Receives a pointer to the VMR.
)
{
IVMRFilterConfig9 * pConfig = NULL;
IVMRWindowlessControl9 *pWC = NULL;
// Set the rendering mode.
HRESULT hr = pVMR->QueryInterface(IID_PPV_ARGS(&pConfig));
if (FAILED(hr))
{
goto done;
}
hr = pConfig->SetRenderingMode(VMR9Mode_Windowless);
if (FAILED(hr))
{
goto done;
}
// Query for the windowless control interface.
hr = pVMR->QueryInterface(IID_PPV_ARGS(&pWC));
if (FAILED(hr))
{
goto done;
}
// Set the clipping window.
hr = pWC->SetVideoClippingWindow(hwnd);
if (FAILED(hr))
{
goto done;
}
// Preserve aspect ratio by letter-boxing
hr = pWC->SetAspectRatioMode(VMR9ARMode_LetterBox);
if (FAILED(hr))
{
goto done;
}
// Return the IVMRWindowlessControl pointer to the caller.
*ppWC = pWC;
(*ppWC)->AddRef();
done:
SafeRelease(&pConfig);
SafeRelease(&pWC);
return hr;
}
O CVMR9::FinalizeGraph
método verifica se o filtro VMR-9 está conectado e, caso contrário, o remove do grafo de filtro.
HRESULT CVMR9::FinalizeGraph(IGraphBuilder *pGraph)
{
if (m_pWindowless == NULL)
{
return S_OK;
}
IBaseFilter *pFilter = NULL;
HRESULT hr = m_pWindowless->QueryInterface(IID_PPV_ARGS(&pFilter));
if (FAILED(hr))
{
goto done;
}
BOOL bRemoved;
hr = RemoveUnconnectedRenderer(pGraph, pFilter, &bRemoved);
// If we removed the VMR, then we also need to release our
// pointer to the VMR's windowless control interface.
if (bRemoved)
{
SafeRelease(&m_pWindowless);
}
done:
SafeRelease(&pFilter);
return hr;
}
Filtro VMR-7
As etapas para o filtro VMR-7 são quase idênticas às da VMR-9, exceto que as interfaces VMR-7 são usadas. O código a seguir cria o filtro VMR-7 e o adiciona ao grafo de filtro.
HRESULT CVMR7::AddToGraph(IGraphBuilder *pGraph, HWND hwnd)
{
IBaseFilter *pVMR = NULL;
HRESULT hr = AddFilterByCLSID(pGraph, CLSID_VideoMixingRenderer,
&pVMR, L"VMR-7");
if (SUCCEEDED(hr))
{
// Set windowless mode on the VMR. This must be done before the VMR
// is connected.
hr = InitWindowlessVMR(pVMR, hwnd, &m_pWindowless);
}
SafeRelease(&pVMR);
return hr;
}
A InitWindowlessVMR
função inicializa a VMR-7 para o modo sem janelas. Essa função executa as etapas a seguir.
- Consulta o filtro VMR-7 para a interface IVMRFilterConfig .
- Chama o método IVMRFilterConfig::SetRenderingMode para definir o modo sem janelas.
- Consulta o filtro VMR-7 para a interface IVMRWindowlessControl .
- Chama o método IVMRWindowlessControl::SetVideoClippingWindow para definir a janela de vídeo.
- Chama o método IVMRWindowlessControl::SetAspectRatioMode para preservar a taxa de proporção de vídeo.
O código a seguir mostra a InitWindowlessVMR
função .
HRESULT InitWindowlessVMR(
IBaseFilter *pVMR, // Pointer to the VMR
HWND hwnd, // Clipping window
IVMRWindowlessControl** ppWC // Receives a pointer to the VMR.
)
{
IVMRFilterConfig* pConfig = NULL;
IVMRWindowlessControl *pWC = NULL;
// Set the rendering mode.
HRESULT hr = pVMR->QueryInterface(IID_PPV_ARGS(&pConfig));
if (FAILED(hr))
{
goto done;
}
hr = pConfig->SetRenderingMode(VMRMode_Windowless);
if (FAILED(hr))
{
goto done;
}
// Query for the windowless control interface.
hr = pVMR->QueryInterface(IID_PPV_ARGS(&pWC));
if (FAILED(hr))
{
goto done;
}
// Set the clipping window.
hr = pWC->SetVideoClippingWindow(hwnd);
if (FAILED(hr))
{
goto done;
}
// Preserve aspect ratio by letter-boxing
hr = pWC->SetAspectRatioMode(VMR_ARMODE_LETTER_BOX);
if (FAILED(hr))
{
goto done;
}
// Return the IVMRWindowlessControl pointer to the caller.
*ppWC = pWC;
(*ppWC)->AddRef();
done:
SafeRelease(&pConfig);
SafeRelease(&pWC);
return hr;
}
O CVMR7::FinalizeGraph
método verifica se o filtro VMR-7 está conectado e, caso contrário, o remove do grafo de filtro.
HRESULT CVMR7::FinalizeGraph(IGraphBuilder *pGraph)
{
if (m_pWindowless == NULL)
{
return S_OK;
}
IBaseFilter *pFilter = NULL;
HRESULT hr = m_pWindowless->QueryInterface(IID_PPV_ARGS(&pFilter));
if (FAILED(hr))
{
goto done;
}
BOOL bRemoved;
hr = RemoveUnconnectedRenderer(pGraph, pFilter, &bRemoved);
// If we removed the VMR, then we also need to release our
// pointer to the VMR's windowless control interface.
if (bRemoved)
{
SafeRelease(&m_pWindowless);
}
done:
SafeRelease(&pFilter);
return hr;
}
Tópicos relacionados