Usando o modo sem janelas
[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.]
O Filtro do Renderizador de Combinação de Vídeo 7 (VMR-7) e o VMR-9 (Video Mixing Renderer Filter 9 ) dão suporte ao modo sem janelas, o que representa uma grande melhoria em relação à interface IVideoWindow . Este tópico descreve as diferenças entre o modo sem janelas e o modo em janelas e como usar o modo sem janelas.
Para permanecer compatível com versões anteriores com aplicativos existentes, a VMR usa como padrão o modo em janelas. No modo de janela, o renderizador cria sua própria janela para exibir o vídeo. Normalmente, o aplicativo define a janela de vídeo como um filho da janela do aplicativo. No entanto, a existência de uma janela de vídeo separada causa alguns problemas:
- O mais importante é que há um potencial para deadlocks se as mensagens de janela forem enviadas entre threads.
- O Gerenciador de Grafo de Filtro deve encaminhar determinadas mensagens de janela, como WM_PAINT, para o Renderizador de Vídeo. O aplicativo deve usar a implementação do Gerenciador de Grafo de Filtro de IVideoWindow (e não do Renderizador de Vídeo), para que o Gerenciador de Grafo de Filtro mantenha o estado interno correto.
- Para receber eventos de mouse ou teclado da janela de vídeo, o aplicativo deve definir um dreno de mensagens, fazendo com que a janela de vídeo encaminhe essas mensagens para o aplicativo.
- Para evitar problemas de recorte, a janela de vídeo deve ter os estilos de janela certos.
O modo sem janela evita esses problemas fazendo com que a VMR desenhe diretamente na área de cliente da janela do aplicativo, usando DirectDraw para recortar o retângulo de vídeo. O modo sem janelas reduz significativamente a chance de deadlocks. Além disso, o aplicativo não precisa definir a janela do proprietário ou os estilos de janela. Na verdade, quando a VMR está no modo sem janelas, ela nem mesmo expõe a interface IVideoWindow , que não é mais necessária.
Para usar o modo sem janelas, você deve configurar explicitamente a VMR. No entanto, você descobrirá que é mais flexível e fácil de usar do que o modo em janelas.
O filtro VMR-7 e o filtro VMR-9 expõem interfaces diferentes, mas as etapas são equivalentes para cada uma.
Configurar a VMR para o modo sem janelas
Para substituir o comportamento padrão da VMR, configure a VMR antes de criar o grafo de filtro:
VMR-7
- Crie o Gerenciador de Grafo de Filtro.
- Crie a VMR-7 e adicione-a ao grafo de filtro.
- Chame IVMRFilterConfig::SetRenderingMode na VMR-7 com o sinalizador VMRMode_Windowless .
- Consulte a VMR-7 para a interface IVMRWindowlessControl .
- Chame IVMRWindowlessControl::SetVideoClippingWindow na VMR-7. Especifique um identificador para a janela em que o vídeo deve aparecer.
VMR-9
- Crie o Gerenciador de Grafo de Filtro.
- Crie a VMR-9 e adicione-a ao grafo de filtro.
- Chame IVMRFilterConfig9::SetRenderingMode na VMR-9 com o sinalizador VMR9Mode_Windowless .
- Consulte a VMR-9 para a interface IVMRWindowlessControl9 .
- Chame IVMRWindowlessControl9::SetVideoClippingWindow na VMR-9. Especifique um identificador para a janela em que o vídeo deve aparecer.
Agora, crie o restante do grafo de filtro chamando IGraphBuilder::RenderFile ou outros métodos de criação de grafo. O Gerenciador de Grafo de Filtro usa automaticamente a instância da VMR que você adicionou ao grafo. (Para obter detalhes sobre por que isso acontece, consulte Conexão Inteligente.)
O código a seguir mostra uma função auxiliar que cria a VMR-7, adiciona-a ao grafo e configura o modo sem janelas.
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;
}
Essa função pressupõe que esteja exibindo apenas um fluxo de vídeo e não esteja misturando um bitmap estático sobre o vídeo. Para obter detalhes, consulte Modo sem janelas da VMR. Você chamaria essa função da seguinte maneira:
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();
}
Posicionar o vídeo
Depois de configurar a VMR, a próxima etapa é definir a posição do vídeo. Há dois retângulos a serem considerados, o retângulo de origem e o retângulo de destino . O retângulo de origem define qual parte do vídeo será exibida. O retângulo de destino especifica a região na área de cliente da janela que conterá o vídeo. A VMR corta a imagem de vídeo para o retângulo de origem e alonga a imagem cortada para caber no retângulo de destino.
VMR-7
- Chame o método IVMRWindowlessControl::SetVideoPosition para especificar ambos os retângulos.
- O retângulo de origem deve ser igual ou menor que o tamanho do vídeo nativo; você pode usar o método IVMRWindowlessControl::GetNativeVideoSize para obter o tamanho do vídeo nativo.
VMR-9
- Chame o método IVMRWindowlessControl9::SetVideoPosition para especificar ambos os retângulos.
- O retângulo de origem deve ser igual ou menor que o tamanho do vídeo nativo; você pode usar o método IVMRWindowlessControl9::GetNativeVideoSize para obter o tamanho do vídeo nativo.
Por exemplo, o código a seguir define os retângulos de origem e destino para a VMR-7. Ele define o retângulo de origem igual a toda a imagem de vídeo e o retângulo de destino igual à área inteira do cliente da janela:
// 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);
}
Se você quiser que o vídeo ocupe uma parte menor da área do cliente, modifique o parâmetro rcDest . Se você quiser cortar a imagem de vídeo, modifique o parâmetro rcSrc .
Manipular mensagens de janela
Como a VMR não tem sua própria janela, ela deve ser notificada se precisar repintar ou redimensionar o vídeo. Responda às seguintes mensagens de janela chamando os métodos de VMR listados.
VMR-7
- WM_PAINT. Chame IVMRWindowlessControl::RepaintVideo. Esse método faz com que a VMR-7 repinta o quadro de vídeo mais recente.
- WM_DISPLAYCHANGE: chame IVMRWindowlessControl::D isplayModeChanged. Esse método notifica a VMR-7 de que o vídeo deve ser mostrado em uma nova resolução ou profundidade de cor.
- WM_SIZE ou WM_WINDOWPOSCHANGED: recalcule a posição do vídeo e chame IVMRWindowlessControl::SetVideoPosition para atualizar a posição, se necessário.
VMR-9
- WM_PAINT. Chame IVMRWindowlessControl9::RepaintVideo. Esse método faz com que a VMR-9 repinta o quadro de vídeo mais recente.
- WM_DISPLAYCHANGE: chame IVMRWindowlessControl9::D isplayModeChanged. Esse método notifica a VMR-9 de que o vídeo deve ser mostrado em uma nova resolução ou profundidade de cor.
- WM_SIZE ou WM_WINDOWPOSCHANGED: recalcule a posição do vídeo e chame IVMRWindowlessControl9::SetVideoPosition para atualizar a posição, se necessário.
Observação
O manipulador padrão da mensagem WM_WINDOWPOSCHANGED envia uma mensagem WM_SIZE . Mas se o aplicativo intercepta WM_WINDOWPOSCHANGED e não o passa para DefWindowProc, você deve chamar SetVideoPosition em seu manipulador de WM_WINDOWPOSCHANGED , além do manipulador de WM_SIZE .
O exemplo a seguir mostra um manipulador de mensagens WM_PAINT . Ele pinta uma região definida pelo retângulo do cliente menos o retângulo de vídeo. Não desenhe no retângulo de vídeo, pois a VMR pintará sobre ele, causando cintilação. Pelo mesmo motivo, não defina um pincel de plano de fundo em sua classe de janela.
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);
}
Embora você precise responder a mensagens WM_PAINT , não há nada que você precise fazer entre WM_PAINT mensagens para atualizar o vídeo. Como mostra este exemplo, o modo sem janelas permite tratar a imagem de vídeo simplesmente como uma região auto-desenhada na janela.
Tópicos relacionados