Compartilhar via


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

  1. Crie o Gerenciador de Grafo de Filtro.
  2. Crie a VMR-7 e adicione-a ao grafo de filtro.
  3. Chame IVMRFilterConfig::SetRenderingMode na VMR-7 com o sinalizador VMRMode_Windowless .
  4. Consulte a VMR-7 para a interface IVMRWindowlessControl .
  5. Chame IVMRWindowlessControl::SetVideoClippingWindow na VMR-7. Especifique um identificador para a janela em que o vídeo deve aparecer.

VMR-9

  1. Crie o Gerenciador de Grafo de Filtro.
  2. Crie a VMR-9 e adicione-a ao grafo de filtro.
  3. Chame IVMRFilterConfig9::SetRenderingMode na VMR-9 com o sinalizador VMR9Mode_Windowless .
  4. Consulte a VMR-9 para a interface IVMRWindowlessControl9 .
  5. 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

  1. Chame o método IVMRWindowlessControl::SetVideoPosition para especificar ambos os retângulos.
  2. 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

  1. Chame o método IVMRWindowlessControl9::SetVideoPosition para especificar ambos os retângulos.
  2. 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

  1. WM_PAINT. Chame IVMRWindowlessControl::RepaintVideo. Esse método faz com que a VMR-7 repinta o quadro de vídeo mais recente.
  2. 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.
  3. 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

  1. WM_PAINT. Chame IVMRWindowlessControl9::RepaintVideo. Esse método faz com que a VMR-9 repinta o quadro de vídeo mais recente.
  2. 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.
  3. 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.

Usando o Renderizador de Combinação de Vídeos

Renderização de vídeo