Partilhar via


Usando o modo sem janela

[O recurso associado a esta página, DirectShow, é um recurso herdado. Foi substituído por MediaPlayer, IMFMediaEnginee Audio/Video Capture in Media Foundation. Esses recursos foram otimizados para Windows 10 e Windows 11. A Microsoft recomenda vivamente que o novo código utilize MediaPlayer, IMFMediaEngine e Captura de Áudio/Vídeo no 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.]

Tanto o Video Mixing Renderer Filter 7 (VMR-7) quanto o Video Mixing Renderer Filter 9 (VMR-9) suportam modo sem janela, o que representa uma grande melhoria em relação à interfaceIVideoWindow. pt-PT: Este tópico descreve as diferenças entre o modo sem janela e o modo com janela, e como usar o modo sem janela.

Para permanecer compatível com aplicações existentes, o VMR utiliza o modo de janela como padrão. 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. A existência de uma janela de vídeo separada causa alguns problemas, no entanto:

  • Mais importante ainda, há potencial para bloqueios se as mensagens de janelas forem enviadas entre threads.
  • O Gerenciador de gráficos 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 Filter Graph Manager do IVideoWindow (e não do Video Renderer), para que o Filter Graph Manager mantenha o estado interno correto.
  • Para receber eventos de mouse ou teclado da janela de vídeo, o aplicativo deve definir um canal de mensagens, permitindo 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 corretos.

O modo sem janela evita esses problemas fazendo com que o VMR desenhe diretamente na área do cliente da janela do aplicativo, usando o DirectDraw para recortar o retângulo de vídeo. O modo sem janelas reduz significativamente a chance de bloqueios. Além disso, o aplicativo não precisa definir a janela do proprietário ou os estilos de janela. Na verdade, quando o VMR está no modo sem janelas, ele nem mesmo expõe o interface IVideoWindow, que não é mais necessária.

Para usar o modo sem janelas, você deve configurar explicitamente o VMR. No entanto, você descobrirá que é mais flexível e mais fácil de usar do que o modo em janela.

O filtro VMR-7 e o filtro VMR-9 expõem interfaces diferentes, mas as etapas são equivalentes para cada uma.

Configurar o VMR para o modo sem janela

Para substituir o comportamento padrão do VMR, configure o VMR antes de criar o gráfico de filtro:

VMR-7

  1. Crie o Gestor de Gráfico de Filtro.
  2. Crie o VMR-7 e adicione-o ao gráfico de filtro.
  3. Chame IVMRFilterConfig::SetRenderingMode no VMR-7 com a bandeira VMRMode_Windowless.
  4. Consulte o VMR-7 para a interface IVMRWindowlessControl.
  5. Chamar IVMRWindowlessControl::SetVideoClippingWindow no VMR-7. Especifique uma alça para a janela onde o vídeo deve aparecer.

VMR-9

  1. Crie o Gestor de Gráficos de Filtro.
  2. Crie o VMR-9 e adicione-o ao gráfico de filtro.
  3. Chame IVMRFilterConfig9::SetRenderingMode no VMR-9 com a bandeira VMR9Mode_Windowless.
  4. Consulte o VMR-9 para a interface IVMRWindowlessControl9.
  5. Chame IVMRWindowlessControl9::SetVideoClippingWindow no VMR-9. Especifique uma alça para a janela onde o vídeo deve aparecer.

Agora, crie o restante do gráfico de filtro chamando IGraphBuilder::RenderFile ou outros métodos de criação de gráficos. O Filter Graph Manager usa automaticamente a instância do VMR que você adicionou ao gráfico. (Para obter detalhes sobre por que isso acontece, consulte Intelligent Connect.)

O código a seguir mostra uma função auxiliar que cria o VMR-7, adiciona-o ao gráfico 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; 
} 

Esta função pressupõe que estão exibindo apenas um fluxo de vídeo e não estão misturando um bitmap estático sobre o vídeo. Para obter detalhes, consulte Modo VMR sem janela. 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();
}

Posicione o vídeo

Depois de configurar o VMR, o próximo passo é definir a posição do vídeo. Há dois retângulos a considerar, o retângulo de fonte e o retângulo de destino . O retângulo de origem define qual parte do vídeo deve ser exibida. O retângulo de destino especifica a região na área do cliente da janela que conterá o vídeo. O VMR corta a imagem de vídeo para o retângulo de origem e estica a imagem cortada para se ajustar ao retângulo de destino.

VMR-7

  1. Chame o IVMRWindowlessControl::SetVideoPosition método 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 IVMRWindowlessControl::GetNativeVideoSize método para obter o tamanho de 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 IVMRWindowlessControl9::GetNativeVideoSize método para obter o tamanho de vídeo nativo.

Por exemplo, o código a seguir define os retângulos de origem e destino para o VMR-7. Ele define o retângulo de origem igual a toda a imagem de vídeo e o retângulo de destino igual a toda a área 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 rcSrc parâmetro.

Manipular mensagens da janela

Como o VMR não tem sua própria janela, ele deve ser notificado se precisar repintar ou redimensionar o vídeo. Responda às seguintes mensagens de janela chamando os métodos VMR listados.

VMR-7

  1. WM_PAINT. Ligue IVMRWindowlessControl::RepaintVideo. Este método faz com que o VMR-7 redesenhe o frame de vídeo mais recente.
  2. WM_DISPLAYCHANGE: Call IVMRWindowlessControl::DisplayModeChanged. Este método notifica o VMR-7 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. Chamada IVMRWindowlessControl9::RepaintVideo. Esse método faz com que o VMR-9 repinte o quadro de vídeo mais recente.
  2. WM_DISPLAYCHANGE: Call IVMRWindowlessControl9::DisplayModeChanged. Este método notifica o VMR-9 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 ligue IVMRWindowlessControl9::SetVideoPosition para atualizar a posição, se necessário.

Observação

O manipulador padrão para a mensagem WM_WINDOWPOSCHANGED envia uma mensagem WM_SIZE. Mas se o seu aplicativo interceptar WM_WINDOWPOSCHANGED e não passá-lo para DefWindowProc, você deve chamar SetVideoPosition no seu manipulador de WM_WINDOWPOSCHANGED, além do seu 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, porque o VMR pintará sobre ele, causando cintilação. Pelo mesmo motivo, não defina um pincel de fundo na 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ê deva responder a WM_PAINT mensagens, não há nada que você precise fazer entre WM_PAINT mensagens para atualizar o vídeo. Como mostra este exemplo, o modo sem janela permite tratar a imagem de vídeo simplesmente como uma região de desenho automático na janela.

Usando o renderizador de mistura de vídeo

Renderização de vídeo