Utilisation du mode sans fenêtre

[La fonctionnalité associée à cette page, DirectShow, est une fonctionnalité héritée. Il a été remplacé par MediaPlayer, IMFMediaEngine et Audio/Video Capture dans Media Foundation. Ces fonctionnalités ont été optimisées pour Windows 10 et Windows 11. Microsoft recommande vivement au nouveau code d’utiliser MediaPlayer, IMFMediaEngine et La capture audio/vidéo dans Media Foundation au lieu de DirectShow, lorsque cela est possible. Microsoft suggère que le code existant qui utilise les API héritées soit réécrit pour utiliser les nouvelles API si possible.]

Le filtre de rendu de mixage vidéo 7 (VMR-7) et le filtre de rendu de mixage vidéo 9 (VMR-9) prennent en charge le mode sans fenêtre, ce qui représente une amélioration majeure par rapport à l’interface IVideoWindow . Cette rubrique décrit les différences entre le mode sans fenêtre et le mode fenêtré, ainsi que l’utilisation du mode sans fenêtre.

Pour rester rétrocompatible avec les applications existantes, le VMR utilise par défaut le mode fenêtré. En mode fenêtré, le convertisseur crée sa propre fenêtre pour afficher la vidéo. En règle générale, l’application définit la fenêtre vidéo comme étant un enfant de la fenêtre d’application. Toutefois, l’existence d’une fenêtre vidéo distincte pose certains problèmes :

  • Plus important encore, il existe un risque d’interblocage si des messages de fenêtre sont envoyés entre les threads.
  • Le Gestionnaire de graphiques de filtre doit transférer certains messages de fenêtre, tels que WM_PAINT, au convertisseur vidéo. L’application doit utiliser l’implémentation d’IVideoWindow du Gestionnaire de graphes de filtre (et non celle du convertisseur vidéo), afin que le Gestionnaire de graphes de filtre conserve l’état interne correct.
  • Pour recevoir des événements de souris ou de clavier à partir de la fenêtre vidéo, l’application doit définir un drain de messages, ce qui entraîne le transfert de ces messages à l’application par la fenêtre vidéo.
  • Pour éviter les problèmes de découpage, la fenêtre vidéo doit avoir les styles de fenêtre appropriés.

Le mode sans fenêtre évite ces problèmes en faisant dessiner le VMR directement sur la zone cliente de la fenêtre d’application, en utilisant DirectDraw pour couper le rectangle vidéo. Le mode sans fenêtre réduit considérablement le risque d’interblocages. En outre, l’application n’a pas besoin de définir la fenêtre propriétaire ou les styles de fenêtre. En fait, lorsque le VMR est en mode sans fenêtre, il n’expose même pas l’interface IVideoWindow , qui n’est plus nécessaire.

Pour utiliser le mode sans fenêtre, vous devez configurer explicitement le VMR. Toutefois, vous constaterez que c’est plus flexible et plus facile à utiliser que le mode fenêtré.

Le filtre VMR-7 et le filtre VMR-9 exposent différentes interfaces, mais les étapes sont équivalentes pour chacune d’elles.

Configurer vmr pour le mode sans fenêtre

Pour remplacer le comportement par défaut du VMR, configurez le VMR avant de créer le graphe de filtre :

VMR-7

  1. Créez le Gestionnaire de graphe de filtre.
  2. Créez le VMR-7 et ajoutez-le au graphique de filtre.
  3. Appelez IVMRFilterConfig::SetRenderingMode sur le VMR-7 avec l’indicateur VMRMode_Windowless .
  4. Interrogez vmR-7 pour l’interface IVMRWindowlessControl .
  5. Appelez IVMRWindowlessControl::SetVideoClippingWindow sur le VMR-7. Spécifiez un handle dans la fenêtre où la vidéo doit apparaître.

VMR-9

  1. Créez le Gestionnaire de graphe de filtre.
  2. Créez le VMR-9 et ajoutez-le au graphique de filtre.
  3. Appelez IVMRFilterConfig9::SetRenderingMode sur le VMR-9 avec l’indicateur VMR9Mode_Windowless .
  4. Interrogez le VMR-9 pour l’interface IVMRWindowlessControl9 .
  5. Appelez IVMRWindowlessControl9::SetVideoClippingWindow sur le VMR-9. Spécifiez un handle dans la fenêtre où la vidéo doit apparaître.

Générez maintenant le reste du graphe de filtre en appelant IGraphBuilder::RenderFile ou d’autres méthodes de création de graphiques. Le Gestionnaire de graphe de filtre utilise automatiquement les instance du vmR que vous avez ajouté au graphique. (Pour plus d’informations sur la raison pour laquelle cela se produit, consultez Intelligent Connect.)

Le code suivant montre une fonction d’assistance qui crée le VMR-7, l’ajoute au graphique et configure le mode sans fenêtre.

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

Cette fonction suppose qu’ils n’affichent qu’un seul flux vidéo et ne mélangent pas une bitmap statique sur la vidéo. Pour plus d’informations, consultez Mode sans fenêtre VMR. Vous devez appeler cette fonction comme suit :

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

Positionner la vidéo

Après avoir configuré le VMR, l’étape suivante consiste à définir la position de la vidéo. Il existe deux rectangles à prendre en compte, le rectangle source et le rectangle de destination . Le rectangle source définit la partie de la vidéo à afficher. Le rectangle de destination spécifie la région dans la zone cliente de la fenêtre qui contiendra la vidéo. Le VMR rogne l’image vidéo dans le rectangle source et étend l’image rognée pour qu’elle s’adapte au rectangle de destination.

VMR-7

  1. Appelez la méthode IVMRWindowlessControl::SetVideoPosition pour spécifier les deux rectangles.
  2. Le rectangle source doit être égal ou inférieur à la taille de la vidéo native ; vous pouvez utiliser la méthode IVMRWindowlessControl::GetNativeVideoSize pour obtenir la taille de la vidéo native.

VMR-9

  1. Appelez la méthode IVMRWindowlessControl9::SetVideoPosition pour spécifier les deux rectangles.
  2. Le rectangle source doit être égal ou inférieur à la taille de la vidéo native ; vous pouvez utiliser la méthode IVMRWindowlessControl9::GetNativeVideoSize pour obtenir la taille de la vidéo native.

Par exemple, le code suivant définit les rectangles source et de destination pour le VMR-7. Il définit le rectangle source égal à l’image vidéo entière et le rectangle de destination égal à l’ensemble de la zone cliente de la fenêtre :

// 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 vous souhaitez que la vidéo occupe une plus petite partie de la zone cliente, modifiez le paramètre rcDest . Si vous souhaitez rogner l’image vidéo, modifiez le paramètre rcSrc .

Gérer les messages de fenêtre

Étant donné que le VMR n’a pas sa propre fenêtre, il doit être averti s’il doit repeindre ou redimensionner la vidéo. Répondez aux messages de fenêtre suivants en appelant les méthodes VMR répertoriées.

VMR-7

  1. WM_PAINT. Appelez IVMRWindowlessControl::RepaintVideo. Cette méthode permet au VMR-7 de repeindre l’image vidéo la plus récente.
  2. WM_DISPLAYCHANGE : Appelez IVMRWindowlessControl::D isplayModeChanged. Cette méthode avertit le VMR-7 que la vidéo doit être affichée à une nouvelle résolution ou profondeur de couleur.
  3. WM_SIZE ou WM_WINDOWPOSCHANGED : recalculez la position de la vidéo et appelez IVMRWindowlessControl::SetVideoPosition pour mettre à jour la position, si nécessaire.

VMR-9

  1. WM_PAINT. Appelez IVMRWindowlessControl9::RepaintVideo. Cette méthode entraîne le repeint du VMR-9 l’image vidéo la plus récente.
  2. WM_DISPLAYCHANGE : Appelez IVMRWindowlessControl9::D isplayModeChanged. Cette méthode avertit le VMR-9 que la vidéo doit être affichée à une nouvelle résolution ou profondeur de couleur.
  3. WM_SIZE ou WM_WINDOWPOSCHANGED : recalculez la position de la vidéo et appelez IVMRWindowlessControl9::SetVideoPosition pour mettre à jour la position, si nécessaire.

Notes

Le gestionnaire par défaut du message WM_WINDOWPOSCHANGED envoie un message WM_SIZE . Mais si votre application intercepte WM_WINDOWPOSCHANGED et ne le transmet pas à DefWindowProc, vous devez appeler SetVideoPosition dans votre gestionnaire de WM_WINDOWPOSCHANGED , en plus de votre gestionnaire de WM_SIZE .

 

L’exemple suivant montre un gestionnaire de messages WM_PAINT . Il peint une région définie par le rectangle client moins le rectangle vidéo. Ne dessinez pas sur le rectangle vidéo, car le VMR peindra dessus, ce qui provoque un scintillement. Pour la même raison, ne définissez pas de pinceau d’arrière-plan dans votre classe de fenêtre.

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

Bien que vous deviez répondre à WM_PAINT messages, vous n’avez rien à faire entre WM_PAINT messages pour mettre à jour la vidéo. Comme le montre cet exemple, le mode sans fenêtre vous permet de traiter l’image vidéo simplement comme une zone de dessin automatique sur la fenêtre.

Utilisation du convertisseur de mixage vidéo

Rendu vidéo