창 없는 모드 사용
[이 페이지와 연결된 기능인 DirectShow는 레거시 기능입니다. MediaPlayer, IMFMediaEngine 및 Media Foundation의 오디오/비디오 캡처로 대체되었습니다. 이러한 기능은 Windows 10 및 Windows 11 최적화되었습니다. 가능한 경우 새 코드가 DirectShow 대신 Media Foundation에서 MediaPlayer, IMFMediaEngine 및 오디오/비디오 캡처를 사용하는 것이 좋습니다. 가능한 경우 레거시 API를 사용하는 기존 코드를 다시 작성하여 새 API를 사용하도록 제안합니다.]
비디오 혼합 렌더러 필터 7(VMR-7)과 비디오 혼합 렌더러 필터 9(VMR-9)는 모두 IVideoWindow 인터페이스보다 크게 개선된 창 없는 모드를 지원합니다. 이 항목에서는 창 없는 모드와 창 없는 모드의 차이점과 창 없는 모드를 사용하는 방법에 대해 설명합니다.
기존 애플리케이션과 이전 버전과 호환되는 상태를 유지하기 위해 VMR은 기본적으로 창 모드로 설정됩니다. 창 모드에서 렌더러는 비디오를 표시하는 자체 창을 만듭니다. 일반적으로 애플리케이션은 비디오 창을 애플리케이션 창의 자식으로 설정합니다. 그러나 별도의 비디오 창이 있으면 몇 가지 문제가 발생합니다.
- 가장 중요한 것은 스레드 간에 창 메시지를 보내는 경우 교착 상태가 발생할 수 있다는 것입니다.
- 필터 그래프 관리자는 WM_PAINT 같은 특정 창 메시지를 Video Renderer에 전달해야 합니다. 필터 Graph 관리자가 올바른 내부 상태를 유지 관리하려면 애플리케이션에서 Filter Graph Manager의 IVideoWindow 구현(Video Renderer가 아닌)을 사용해야 합니다.
- 비디오 창에서 마우스 또는 키보드 이벤트를 받으려면 애플리케이션에서 메시지 드레이닝 을 설정해야 하므로 비디오 창에서 이러한 메시지를 애플리케이션에 전달합니다.
- 클리핑 문제를 방지하려면 비디오 창에 올바른 창 스타일이 있어야 합니다.
창 없는 모드는 VMR이 애플리케이션 창의 클라이언트 영역에 직접 그려지도록 하고 DirectDraw를 사용하여 비디오 사각형을 클리핑하여 이러한 문제를 방지합니다. 창 없는 모드는 교착 상태의 가능성을 크게 줄입니다. 또한 애플리케이션은 소유자 창 또는 창 스타일을 설정할 필요가 없습니다. 실제로 VMR이 창 없는 모드인 경우 더 이상 필요하지 않은 IVideoWindow 인터페이스도 노출하지 않습니다.
창 없는 모드를 사용하려면 VMR을 명시적으로 구성해야 합니다. 그러나 창 모드보다 더 유연하고 사용하기 쉽습니다.
VMR-7 필터와 VMR-9 필터는 서로 다른 인터페이스를 노출하지만 단계는 각각에 해당합니다.
창 없는 모드에 대한 VMR 구성
VMR의 기본 동작을 재정의하려면 필터 그래프를 빌드하기 전에 VMR을 구성합니다.
VMR-7
- 필터 그래프 관리자를 만듭니다.
- VMR-7을 만들고 필터 그래프에 추가합니다.
- VMR-7에서 VMRMode_Windowless 플래그를 사용하여 IVMRFilterConfig::SetRenderingMode를 호출합니다.
- IVMRWindowlessControl 인터페이스에 대한 VMR-7을 쿼리합니다.
- VMR-7에서 IVMRWindowlessControl::SetVideoClippingWindow를 호출합니다. 비디오가 표시되어야 하는 창에 대한 핸들을 지정합니다.
VMR-9
- 필터 그래프 관리자를 만듭니다.
- VMR-9를 만들고 필터 그래프에 추가합니다.
- VMR-9에서 VMR9Mode_Windowless 플래그를 사용하여 IVMRFilterConfig9::SetRenderingMode를 호출합니다.
- IVMRWindowlessControl9 인터페이스에 대한 VMR-9를 쿼리합니다.
- VMR-9에서 IVMRWindowlessControl9::SetVideoClippingWindow를 호출합니다. 비디오가 표시되어야 하는 창에 대한 핸들을 지정합니다.
이제 IGraphBuilder::RenderFile 또는 다른 그래프 빌드 메서드를 호출하여 필터 그래프의 나머지 부분을 빌드합니다. Filter Graph Manager는 그래프에 추가한 VMR의 instance 자동으로 사용합니다. 이 문제가 발생하는 이유에 대한 자세한 내용은 Intelligent Connect를 참조하세요.
다음 코드는 VMR-7을 만들고, 그래프에 추가하고, 창 없는 모드를 설정하는 도우미 함수를 보여줍니다.
HRESULT InitWindowlessVMR(
HWND hwndApp, // Window to hold the video.
IGraphBuilder* pGraph, // Pointer to the Filter Graph Manager.
IVMRWindowlessControl** ppWc // Receives a pointer to the VMR.
)
{
if (!pGraph || !ppWc)
{
return E_POINTER;
}
IBaseFilter* pVmr = NULL;
IVMRWindowlessControl* pWc = NULL;
// Create the VMR.
HRESULT hr = CoCreateInstance(CLSID_VideoMixingRenderer, NULL,
CLSCTX_INPROC, IID_IBaseFilter, (void**)&pVmr);
if (FAILED(hr))
{
return hr;
}
// Add the VMR to the filter graph.
hr = pGraph->AddFilter(pVmr, L"Video Mixing Renderer");
if (FAILED(hr))
{
pVmr->Release();
return hr;
}
// Set the rendering mode.
IVMRFilterConfig* pConfig;
hr = pVmr->QueryInterface(IID_IVMRFilterConfig, (void**)&pConfig);
if (SUCCEEDED(hr))
{
hr = pConfig->SetRenderingMode(VMRMode_Windowless);
pConfig->Release();
}
if (SUCCEEDED(hr))
{
// Set the window.
hr = pVmr->QueryInterface(IID_IVMRWindowlessControl, (void**)&pWc);
if( SUCCEEDED(hr))
{
hr = pWc->SetVideoClippingWindow(hwndApp);
if (SUCCEEDED(hr))
{
*ppWc = pWc; // Return this as an AddRef'd pointer.
}
else
{
// An error occurred, so release the interface.
pWc->Release();
}
}
}
pVmr->Release();
return hr;
}
이 함수는 가 하나의 비디오 스트림만 표시하고 있고 비디오 위에 정적 비트맵을 혼합하지 않는다고 가정합니다. 자세한 내용은 VMR 창 없는 모드를 참조하세요. 다음과 같이 이 함수를 호출합니다.
IVMRWindowlessControl *pWc = NULL;
hr = InitWindowlessVMR(hwnd, pGraph, &g_pWc);
if (SUCCEEDED(hr))
{
// Build the graph. For example:
pGraph->RenderFile(wszMyFileName, 0);
// Release the VMR interface when you are done.
pWc->Release();
}
비디오 위치 지정
VMR을 구성한 후 다음 단계는 비디오의 위치를 설정하는 것입니다. 원본 사각형과 대상 사각형의 두 가지 사각형을 고려해야 합니다. 원본 사각형은 표시할 비디오 부분을 정의합니다. 대상 사각형은 비디오를 포함할 창의 클라이언트 영역에 있는 지역을 지정합니다. VMR은 비디오 이미지를 원본 사각형으로 자르고 대상 사각형에 맞게 잘린 이미지를 늘입니다.
VMR-7
- IVMRWindowlessControl::SetVideoPosition 메서드를 호출하여 두 사각형을 지정합니다.
- 원본 사각형은 네이티브 비디오 크기와 같거나 작아야 합니다. IVMRWindowlessControl::GetNativeVideoSize 메서드를 사용하여 네이티브 비디오 크기를 가져올 수 있습니다.
VMR-9
- IVMRWindowlessControl9::SetVideoPosition 메서드를 호출하여 두 사각형을 모두 지정합니다.
- 원본 사각형은 네이티브 비디오 크기와 같거나 작아야 합니다. IVMRWindowlessControl9::GetNativeVideoSize 메서드를 사용하여 네이티브 비디오 크기를 가져올 수 있습니다.
예를 들어 다음 코드는 VMR-7에 대한 원본 및 대상 사각형을 설정합니다. 원본 사각형을 전체 비디오 이미지와 같고 대상 사각형은 전체 창 클라이언트 영역과 동일하게 설정합니다.
// Find the native video size.
long lWidth, lHeight;
HRESULT hr = g_pWc->GetNativeVideoSize(&lWidth, &lHeight, NULL, NULL);
if (SUCCEEDED(hr))
{
RECT rcSrc, rcDest;
// Set the source rectangle.
SetRect(&rcSrc, 0, 0, lWidth, lHeight);
// Get the window client area.
GetClientRect(hwnd, &rcDest);
// Set the destination rectangle.
SetRect(&rcDest, 0, 0, rcDest.right, rcDest.bottom);
// Set the video position.
hr = g_pWc->SetVideoPosition(&rcSrc, &rcDest);
}
비디오를 통해 클라이언트 영역의 작은 부분을 차지하려면 rcDest 매개 변수를 수정합니다. 비디오 이미지를 자르려면 rcSrc 매개 변수를 수정합니다.
창 메시지 처리
VMR에는 자체 창이 없으므로 비디오를 다시 그리거나 크기를 조정해야 하는 경우 알림을 받아야 합니다. 나열된 VMR 메서드를 호출하여 다음 창 메시지에 응답합니다.
VMR-7
- WM_PAINT. IVMRWindowlessControl::RepaintVideo를 호출합니다. 이 메서드를 사용하면 VMR-7이 최신 비디오 프레임을 다시 칠합니다.
- WM_DISPLAYCHANGE: IVMRWindowlessControl::D isplayModeChanged를 호출합니다. 이 메서드는 VMR-7에 비디오가 새로운 해상도 또는 색 깊이로 표시되어야 한다는 것을 알릴 수 있습니다.
- WM_SIZE 또는 WM_WINDOWPOSCHANGED: 비디오의 위치를 다시 계산하고 IVMRWindowlessControl::SetVideoPosition 을 호출하여 필요한 경우 위치를 업데이트합니다.
VMR-9
- WM_PAINT. IVMRWindowlessControl9::RepaintVideo를 호출합니다. 이 메서드를 사용하면 VMR-9가 최신 비디오 프레임을 다시 칠합니다.
- WM_DISPLAYCHANGE: IVMRWindowlessControl9::D isplayModeChanged를 호출합니다. 이 메서드는 VMR-9에 비디오가 새로운 해상도 또는 색 깊이로 표시되어야 한다는 것을 알릴 수 있습니다.
- WM_SIZE 또는 WM_WINDOWPOSCHANGED: 비디오의 위치를 다시 계산하고 IVMRWindowlessControl9::SetVideoPosition 을 호출하여 필요한 경우 위치를 업데이트합니다.
참고
WM_WINDOWPOSCHANGED 메시지의 기본 처리기는 WM_SIZE 메시지를 보냅니다. 그러나 애플리케이션이 WM_WINDOWPOSCHANGED 가로채 DefWindowProc에 전달하지 않는 경우 WM_SIZE 처리기 외에 WM_WINDOWPOSCHANGED 처리기에서 SetVideoPosition을 호출해야 합니다.
다음 예제에서는 WM_PAINT 메시지 처리기를 보여줍니다. 클라이언트 사각형에서 비디오 사각형을 뺀 영역으로 정의됩니다. VMR이 비디오 사각형 위에 그려져 깜박임이 발생하므로 비디오 사각형에 그리지 마세요. 같은 이유로 창 클래스에서 배경 브러시를 설정하지 마세요.
void OnPaint(HWND hwnd)
{
PAINTSTRUCT ps;
HDC hdc;
RECT rcClient;
GetClientRect(hwnd, &rcClient);
hdc = BeginPaint(hwnd, &ps);
if (g_pWc != NULL)
{
// Find the region where the application can paint by subtracting
// the video destination rectangle from the client area.
// (Assume that g_rcDest was calculated previously.)
HRGN rgnClient = CreateRectRgnIndirect(&rcClient);
HRGN rgnVideo = CreateRectRgnIndirect(&g_rcDest);
CombineRgn(rgnClient, rgnClient, rgnVideo, RGN_DIFF);
// Paint on window.
HBRUSH hbr = GetSysColorBrush(COLOR_BTNFACE);
FillRgn(hdc, rgnClient, hbr);
// Clean up.
DeleteObject(hbr);
DeleteObject(rgnClient);
DeleteObject(rgnVideo);
// Request the VMR to paint the video.
HRESULT hr = g_pWc->RepaintVideo(hwnd, hdc);
}
else // There is no video, so paint the whole client area.
{
FillRect(hdc, &rc2, (HBRUSH)(COLOR_BTNFACE + 1));
}
EndPaint(hwnd, &ps);
}
WM_PAINT 메시지에 응답해야 하지만 비디오를 업데이트하기 위해 WM_PAINT 메시지 간에 수행할 작업은 없습니다. 이 예제에서 볼 수 있듯이 창 없는 모드를 사용하면 비디오 이미지를 단순히 창의 자체 그리기 영역으로 처리할 수 있습니다.
관련 항목