4단계: Video Renderer 추가
[이 페이지와 연결된 기능인 DirectShow는 레거시 기능입니다. MediaPlayer, IMFMediaEngine 및 Media Foundation의 오디오/비디오 캡처로 대체되었습니다. 이러한 기능은 Windows 10 및 Windows 11 최적화되었습니다. 가능한 경우 새 코드에서 DirectShow 대신 MediaPlayer, IMFMediaEngine 및 오디오/비디오 캡처를 사용하는 것이 좋습니다. 가능한 경우 레거시 API를 사용하는 기존 코드를 다시 작성하여 새 API를 사용하도록 제안합니다.]
이 항목은 DirectShow의 오디오/비디오 재생 자습서의 4단계입니다. 전체 코드는 DirectShow 재생 예제 항목에 나와 있습니다.
DirectShow는 비디오를 렌더링하는 여러 가지 필터를 제공합니다.
- 향상된 비디오 렌더러 필터 (EVR)
- 비디오 혼합 렌더러 필터 9 (VMR-9)
- 비디오 혼합 렌더러 필터 7 (VMR-7)
이러한 필터 간의 차이점에 대한 자세한 내용은 올바른 비디오 렌더러 선택을 참조하세요.
이 자습서에서 각 비디오 렌더러 필터는 이들 간의 차이점 중 일부를 추상화하는 클래스에 의해 래핑됩니다. 이러한 클래스는 모두 라는 CVideoRenderer
추상 기본 클래스에서 파생됩니다. 선언 CVideoRenderer
은 2단계: CVideoRenderer 선언 및 파생 클래스에 표시됩니다.
다음 메서드는 EVR, VMR-9 및 마지막으로 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;
}
EVR 필터
다음 코드는 EVR 필터를 만들고 필터 그래프에 추가합니다. 이 예제에 사용된 함수 AddFilterByCLSID
는 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;
}
함수는 InitializeEVR
EVR 필터를 초기화합니다. 이 함수는 다음 단계를 수행합니다.
- IMFGetService 인터페이스에 대한 필터를 쿼리합니다.
- IMFGetService::GetService를 호출하여 IMFVideoDisplayControl 인터페이스에 대한 포인터를 가져옵니다.
- IMFVideoDisplayControl::SetVideoWindow를 호출하여 비디오 창을 설정합니다.
- IMFVideoDisplayControl::SetAspectRatioMode를 호출하여 비디오 가로 세로 비율을 유지하도록 EVR을 구성합니다.
다음 코드는 함수를 InitializeEVR
보여줍니다.
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;
}
그래프가 빌드된 후 는 DShowPlayer::RenderStreams
를 호출합니다 CVideoRenderer::FinalizeGraph
. 이 메서드는 최종 초기화 또는 정리를 수행합니다. 다음 코드는 이 메서드의 구현을 CEVR
보여줍니다.
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;
}
EVR이 다른 필터에 연결되지 않은 경우 이 메서드는 그래프에서 EVR을 제거합니다. 미디어 파일에 비디오 스트림이 없는 경우 이 문제가 발생할 수 있습니다.
VMR-9 필터
다음 코드는 VMR-9 필터를 만들고 필터 그래프에 추가합니다.
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;
}
함수는 InitWindowlessVMR9
창 없는 모드에 대해 VMR-9를 초기화합니다. (창 없는 모드에 대한 자세한 내용은 VMR 창 없는 모드를 참조하세요.) 이 함수는 다음 단계를 수행합니다.
- IVMRFilterConfig9 인터페이스에 대한 VMR-9 필터를 쿼리합니다.
- IVMRFilterConfig9::SetRenderingMode 메서드를 호출하여 창 없는 모드를 설정합니다.
- IVMRWindowlessControl9 인터페이스에 대한 VMR-9 필터를 쿼리합니다.
- IVMRWindowlessControl9::SetVideoClippingWindow 메서드를 호출하여 비디오 창을 설정합니다.
- IVMRWindowlessControl9::SetAspectRatioMode 메서드를 호출하여 비디오 가로 세로 비율을 유지합니다.
다음 코드는 함수를 InitWindowlessVMR9
보여줍니다.
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;
}
메서드는 CVMR9::FinalizeGraph
VMR-9 필터가 연결되어 있는지 확인하고, 연결되지 않은 경우 필터 그래프에서 제거합니다.
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;
}
VMR-7 필터
VMR-7 필터의 단계는 VMR-7 인터페이스가 대신 사용된다는 점을 제외하고 VMR-9의 단계와 거의 동일합니다. 다음 코드는 VMR-7 필터를 만들고 필터 그래프에 추가합니다.
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;
}
함수는 InitWindowlessVMR
창 없는 모드에 대해 VMR-7을 초기화합니다. 이 함수는 다음 단계를 수행합니다.
- IVMRFilterConfig 인터페이스에 대한 VMR-7 필터를 쿼리합니다.
- IVMRFilterConfig::SetRenderingMode 메서드를 호출하여 창 없는 모드를 설정합니다.
- IVMRWindowlessControl 인터페이스에 대한 VMR-7 필터를 쿼리합니다.
- IVMRWindowlessControl::SetVideoClippingWindow 메서드를 호출하여 비디오 창을 설정합니다.
- IVMRWindowlessControl::SetAspectRatioMode 메서드를 호출하여 비디오 가로 세로 비율을 유지합니다.
다음 코드는 함수를 InitWindowlessVMR
보여줍니다.
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;
}
메서드는 CVMR7::FinalizeGraph
VMR-7 필터가 연결되어 있는지 확인하고, 연결되지 않은 경우 필터 그래프에서 제거합니다.
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;
}
관련 항목