Paso 4: Agregar el representador de vídeo
[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 4 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.
DirectShow proporciona varios filtros diferentes que representan vídeo:
- Filtro de representador de vídeo mejorado (EVR)
- Filtro de representador de mezcla de vídeo 9 (VMR-9)
- Filtro de representador de mezcla de vídeo 7 (VMR-7)
Para obtener más información sobre las diferencias entre estos filtros, consulte Elección del representador de vídeo correcto.
En este tutorial, cada filtro de representador de vídeo se ajusta mediante una clase que abstrae algunas de las diferencias entre ellos. Todas estas clases derivan de una clase base abstracta denominada CVideoRenderer
. La declaración de CVideoRenderer
se muestra en el paso 2: Declarar CVideoRenderer y Clases derivadas.
El siguiente método intenta crear cada representador de vídeo a su vez, comenzando con el EVR, luego vmR-9 y, por último, 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
El código siguiente crea el filtro EVR y lo agrega al gráfico de filtros. La función AddFilterByCLSID
usada en este ejemplo se muestra en el tema Agregar un 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;
}
La InitializeEVR
función inicializa el filtro EVR. Esta función realiza los pasos siguientes.
- Consulta el filtro de la interfaz IMFGetService .
- Llama a IMFGetService::GetService para obtener un puntero a la interfaz IMFVideoDisplayControl .
- Llama a IMFVideoDisplayControl::SetVideoWindow para establecer la ventana de vídeo.
- Llama a IMFVideoDisplayControl::SetAspectRatioMode para configurar el EVR para conservar la relación de aspecto del vídeo.
En el código siguiente se muestra la InitializeEVR
función .
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;
}
Una vez compilado el grafo, DShowPlayer::RenderStreams
llama a CVideoRenderer::FinalizeGraph
. Este método realiza cualquier inicialización o limpieza finales. En el código siguiente se muestra la CEVR
implementación de este 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;
}
Si el EVR no está conectado a ningún otro filtro, este método quita el EVR del gráfico. Esto puede ocurrir si el archivo multimedia no contiene una secuencia de vídeo.
Filtro VMR-9
El código siguiente crea el filtro VMR-9 y lo agrega al gráfico de filtros.
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;
}
La InitWindowlessVMR9
función inicializa VMR-9 para el modo sin ventanas. (Para obtener más información sobre el modo sin ventana, consulte Modo sin ventana de VMR). Esta función realiza los pasos siguientes.
- Consulta el filtro VMR-9 para la interfaz IVMRFilterConfig9 .
- Llama al método IVMRFilterConfig9::SetRenderingMode para establecer el modo sin ventana.
- Consulta el filtro VMR-9 para la interfaz IVMRWindowlessControl9 .
- Llama al método IVMRWindowlessControl9::SetVideoClippingWindow para establecer la ventana de vídeo.
- Llama al método IVMRWindowlessControl9::SetAspectRatioMode para conservar la relación de aspecto del vídeo.
En el código siguiente se muestra la InitWindowlessVMR9
función .
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;
}
El CVMR9::FinalizeGraph
método comprueba si el filtro VMR-9 está conectado y, si no es así, lo quita del gráfico de filtros.
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
Los pasos para el filtro VMR-7 son casi idénticos a los del VMR-9, excepto las interfaces VMR-7 en su lugar. El código siguiente crea el filtro VMR-7 y lo agrega al gráfico de filtros.
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;
}
La InitWindowlessVMR
función inicializa VMR-7 para el modo sin ventanas. Esta función realiza los pasos siguientes.
- Consulta el filtro VMR-7 para la interfaz IVMRFilterConfig .
- Llama al método IVMRFilterConfig::SetRenderingMode para establecer el modo sin ventana.
- Consulta el filtro VMR-7 para la interfaz IVMRWindowlessControl .
- Llama al método IVMRWindowlessControl::SetVideoClippingWindow para establecer la ventana de vídeo.
- Llama al método IVMRWindowlessControl::SetAspectRatioMode para conservar la relación de aspecto del vídeo.
En el código siguiente se muestra la InitWindowlessVMR
función .
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;
}
El CVMR7::FinalizeGraph
método comprueba si el filtro VMR-7 está conectado y, si no es así, lo quita del gráfico de filtros.
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;
}
Temas relacionados