Uso del modo sin ventanas

[La característica asociada a esta página, DirectShow, es una característica heredada. Se ha reemplazado por MediaPlayer, IMFMediaEngine y Captura de audio/vídeo en Media Foundation. Esas características se han optimizado para Windows 10 y Windows 11. Microsoft recomienda encarecidamente que el nuevo código use MediaPlayer, IMFMediaEngine y Audio/Video Capture en Media Foundation en lugar de DirectShow, siempre que sea posible. Microsoft sugiere que el código existente que usa las API heredadas se reescriba para usar las nuevas API si es posible.

Tanto el filtro de representador de mezcla de vídeos 7 (VMR-7) como el filtro del representador de mezcla de vídeos 9 (VMR-9) admiten el modo sin ventanas, lo que representa una mejora importante sobre la interfaz IVideoWindow . En este tema se describen las diferencias entre el modo sin ventana y el modo de ventana, y cómo usar el modo sin ventanas.

Para seguir siendo compatible con versiones anteriores con las aplicaciones existentes, el valor predeterminado de VMR es el modo con ventanas. En el modo con ventana, el representador crea su propia ventana para mostrar el vídeo. Normalmente, la aplicación establece que la ventana de vídeo sea un elemento secundario de la ventana de la aplicación. Sin embargo, la existencia de una ventana de vídeo independiente provoca algunos problemas:

  • Lo más importante es que existe la posibilidad de interbloqueos si se envían mensajes de ventana entre subprocesos.
  • El Administrador de gráficos de filtros debe reenviar determinados mensajes de ventana, como WM_PAINT, al representador de vídeo. La aplicación debe usar la implementación del Administrador de gráficos de filtros de IVideoWindow (y no de Video Renderer), de modo que el Administrador de gráficos de filtros mantenga el estado interno correcto.
  • Para recibir eventos de mouse o teclado desde la ventana de vídeo, la aplicación debe establecer una purga de mensajes, lo que hace que la ventana de vídeo reenvíe estos mensajes a la aplicación.
  • Para evitar problemas de recorte, la ventana de vídeo debe tener los estilos de ventana correctos.

El modo sin ventanas evita estos problemas al hacer que VMR dibuje directamente en el área cliente de la ventana de la aplicación, mediante DirectDraw para recortar el rectángulo de vídeo. El modo sin ventana reduce significativamente la posibilidad de interbloqueos. Además, la aplicación no tiene que establecer la ventana de propietario ni los estilos de ventana. De hecho, cuando VMR está en modo sin ventanas, ni siquiera expone la interfaz IVideoWindow , que ya no es necesaria.

Para usar el modo sin ventana, debe configurar explícitamente vmR. Sin embargo, encontrará que es más flexible y fácil de usar que el modo con ventanas.

El filtro VMR-7 y el filtro VMR-9 exponen interfaces diferentes, pero los pasos son equivalentes para cada uno.

Configuración de VMR para el modo sin ventanas

Para invalidar el comportamiento predeterminado de VMR, configure VMR antes de compilar el gráfico de filtros:

VMR-7

  1. Cree el Administrador de gráficos de filtros.
  2. Cree VMR-7 y agréguelo al gráfico de filtros.
  3. Llame a IVMRFilterConfig::SetRenderingMode en VMR-7 con la marca VMRMode_Windowless .
  4. Consulte VMR-7 para la interfaz IVMRWindowlessControl .
  5. Llame a IVMRWindowlessControl::SetVideoClippingWindow en VMR-7. Especifique un identificador para la ventana donde debería aparecer el vídeo.

VMR-9

  1. Cree el Administrador de gráficos de filtros.
  2. Cree VMR-9 y agréguelo al gráfico de filtros.
  3. Llame a IVMRFilterConfig9::SetRenderingMode en VMR-9 con la marca de VMR9Mode_Windowless .
  4. Consulte vmR-9 para la interfaz IVMRWindowlessControl9 .
  5. Llame a IVMRWindowlessControl9::SetVideoClippingWindow en VMR-9. Especifique un identificador para la ventana donde debería aparecer el vídeo.

Ahora, compile el resto del gráfico de filtros llamando a IGraphBuilder::RenderFile u otros métodos de creación de grafos. Filter Graph Manager usa automáticamente la instancia de VMR que agregó al grafo. (Para más información sobre por qué ocurre esto, consulte Intelligent Connect).

El código siguiente muestra una función auxiliar que crea vmR-7, la agrega al gráfico y configura el modo sin ventanas.

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 función supone que solo muestra una secuencia de vídeo y no mezcla un mapa de bits estático sobre el vídeo. Para más información, consulte Modo sin ventana de VMR. Llamaría a esta función de la siguiente manera:

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();
}

Colocar el vídeo

Después de configurar VMR, el siguiente paso es establecer la posición del vídeo. Hay dos rectángulos que se deben tener en cuenta , el rectángulo de origen y el rectángulo de destino . El rectángulo de origen define la parte del vídeo que se va a mostrar. El rectángulo de destino especifica la región del área cliente de la ventana que contendrá el vídeo. VMR recorta la imagen de vídeo en el rectángulo de origen y amplía la imagen recortada para ajustarse al rectángulo de destino.

VMR-7

  1. Llame al método IVMRWindowlessControl::SetVideoPosition para especificar ambos rectángulos.
  2. El rectángulo de origen debe ser igual o menor que el tamaño del vídeo nativo; Puede usar el método IVMRWindowlessControl::GetNativeVideoSize para obtener el tamaño de vídeo nativo.

VMR-9

  1. Llame al método IVMRWindowlessControl9::SetVideoPosition para especificar ambos rectángulos.
  2. El rectángulo de origen debe ser igual o menor que el tamaño del vídeo nativo; Puede usar el método IVMRWindowlessControl9::GetNativeVideoSize para obtener el tamaño de vídeo nativo.

Por ejemplo, el código siguiente establece los rectángulos de origen y destino para VMR-7. Establece el rectángulo de origen igual a toda la imagen de vídeo y el rectángulo de destino es igual al área cliente de ventana completa:

// 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); 
}

Si desea que el vídeo ocupe una parte más pequeña del área de cliente, modifique el parámetro rcDest . Si desea recortar la imagen de vídeo, modifique el parámetro rcSrc .

Controlar mensajes de ventana

Dado que VMR no tiene su propia ventana, debe recibir una notificación si necesita volver a pintar o cambiar el tamaño del vídeo. Responda a los siguientes mensajes de ventana llamando a los métodos vmR enumerados.

VMR-7

  1. WM_PAINT. Llame a IVMRWindowlessControl::RepaintVideo. Este método hace que VMR-7 vuelva a dibujar el fotograma de vídeo más reciente.
  2. WM_DISPLAYCHANGE: llame a IVMRWindowlessControl::D isplayModeChanged. Este método notifica al VMR-7 que el vídeo debe mostrarse en una nueva resolución o profundidad de color.
  3. WM_SIZE o WM_WINDOWPOSCHANGED: vuelva a calcular la posición del vídeo y llame a IVMRWindowlessControl::SetVideoPosition para actualizar la posición, si es necesario.

VMR-9

  1. WM_PAINT. Llame a IVMRWindowlessControl9::RepaintVideo. Este método hace que VMR-9 vuelva a dibujar el fotograma de vídeo más reciente.
  2. WM_DISPLAYCHANGE: llame a IVMRWindowlessControl9::D isplayModeChanged. Este método notifica al VMR-9 que el vídeo debe mostrarse en una nueva resolución o profundidad de color.
  3. WM_SIZE o WM_WINDOWPOSCHANGED: vuelva a calcular la posición del vídeo y llame a IVMRWindowlessControl9::SetVideoPosition para actualizar la posición, si es necesario.

Nota:

El controlador predeterminado del mensaje WM_WINDOWPOSCHANGED envía un mensaje de WM_SIZE . Pero si la aplicación intercepta WM_WINDOWPOSCHANGED y no la pasa a DefWindowProc, debe llamar a SetVideoPosition en el controlador de WM_WINDOWPOSCHANGED , además del controlador de WM_SIZE .

 

En el ejemplo siguiente se muestra un controlador de mensajes WM_PAINT . Pinta una región definida por el rectángulo del cliente menos el rectángulo de vídeo. No dibuje en el rectángulo de vídeo, ya que VMR pintará sobre él, lo que provocará parpadeo. Por el mismo motivo, no establezca un pincel de fondo en la clase de ventana.

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); 
} 

Aunque debe responder a WM_PAINT mensajes, no es necesario hacer nada entre WM_PAINT mensajes para actualizar el vídeo. Como se muestra en este ejemplo, el modo sin ventanas le permite tratar la imagen de vídeo simplemente como una región de auto dibujo en la ventana.

Uso del representador de mezcla de vídeos

Representación de vídeo