Verwenden des fensterlosen Modus

Sowohl der Video Mixing Renderer Filter 7 (VMR-7) als auch der Video Mixing Renderer Filter 9 (VMR-9) unterstützen den fensterlosen Modus, der eine wesentliche Verbesserung gegenüber der IVideoWindow-Schnittstelle darstellt. In diesem Thema werden die Unterschiede zwischen dem Fenstermodus und dem Fenstermodus sowie die Verwendung des fensterlosen Modus beschrieben.

Um abwärtskompatibel mit vorhandenen Anwendungen zu bleiben, wird der VMR standardmäßig auf den Fenstermodus festgelegt. Im Fenstermodus erstellt der Renderer ein eigenes Fenster zum Anzeigen des Videos. In der Regel legt die Anwendung das Videofenster als untergeordnetes Fenster des Anwendungsfensters fest. Das Vorhandensein eines separaten Videofensters verursacht jedoch einige Probleme:

  • Vor allem besteht das Potenzial für Deadlocks, wenn Fenstermeldungen zwischen Threads gesendet werden.
  • Der Filter Graph-Manager muss bestimmte Fenstermeldungen, z. B. WM _ PAINT, an den Videorenderer senden. Die Anwendung muss die Implementierung von IVideoWindow Graph Filter Graph Manager und nicht die des Videorenderers verwenden, damit der Filter Graph Manager den richtigen internen Zustand beibe verwaltet.
  • Um Maus- oder Tastaturereignisse aus dem Videofenster zu empfangen, muss die Anwendung eine Meldungsentleerung festlegen, wodurch das Videofenster diese Nachrichten an die Anwendung weitersendet.
  • Um Clippingprobleme zu vermeiden, muss das Videofenster über die richtigen Fensterstile verfügen.

Im fensterlosen Modus werden diese Probleme vermieden, indem die VMR direkt im Clientbereich des Anwendungsfensters gedrapt wird, indem DirectDraw verwendet wird, um das Videorechteck zu beschneiden. Der fensterlose Modus verringert die Wahrscheinlichkeit von Deadlocks erheblich. Außerdem muss die Anwendung weder das Besitzerfenster noch die Fensterstile festlegen. Wenn sich die VMR im fensterlosen Modus befindet, macht sie nicht einmal die IVideoWindow-Schnittstelle verfügbar, die nicht mehr benötigt wird.

Um den fensterlosen Modus zu verwenden, müssen Sie die VMR explizit konfigurieren. Sie werden jedoch feststellen, dass flexibler und einfacher zu verwenden ist als der Fenstermodus.

Der VMR-7-Filter und der VMR-9-Filter machen verschiedene Schnittstellen verfügbar, aber die Schritte sind für jede Schnittstelle äquivalent.

Konfigurieren der VMR für den fensterlosen Modus

Um das Standardverhalten des virtuellen Computers zu überschreiben, konfigurieren Sie die VMR, bevor Sie das Filterdiagramm erstellen:

VMR-7

  1. Erstellen Sie den Filter Graph Manager.
  2. Erstellen Sie VMR-7, und fügen Sie es dem Filterdiagramm hinzu.
  3. Rufen Sie IVMRFilterConfig::SetRenderingMode auf VMR-7 mit dem VMRMode _ Windowless-Flag auf.
  4. Fragen Sie VMR-7 nach der IVMRWindowlessControl-Schnittstelle ab.
  5. Rufen Sie IVMRWindowlessControl::SetVideoClippingWindow auf VMR-7 auf. Geben Sie ein Handle für das Fenster an, in dem das Video angezeigt werden soll.

VMR-9

  1. Erstellen Sie den Filter Graph Manager.
  2. Erstellen Sie VMR-9, und fügen Sie es dem Filterdiagramm hinzu.
  3. Rufen Sie IVMRFilterConfig9::SetRenderingMode auf VMR-9 mit dem Fensterlosen FLAG VMR9Mode _ auf.
  4. Fragen Sie VMR-9 nach der IVMRWindowlessControl9-Schnittstelle ab.
  5. Rufen Sie IVMRWindowlessControl9::SetVideoClippingWindow auf VMR-9 auf. Geben Sie ein Handle für das Fenster an, in dem das Video angezeigt werden soll.

Erstellen Sie nun den Rest des Filterdiagramms, indem Sie IGraphBuilder::RenderFile oder andere Graph-Erstellungsmethoden aufrufen. Der Filter Graph-Manager verwendet automatisch die Instanz der VMR, die Sie dem Diagramm hinzugefügt haben. (Weitere Informationen dazu, warum dies geschieht, finden Sie unter Intelligent Verbinden.)

Der folgende Code zeigt eine Hilfsfunktion, die VMR-7 erstellt, dem Diagramm hinzufügt und den fensterlosen Modus einfügt.

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

Bei dieser Funktion wird davon ausgegangen, dass nur ein Videostream angezeigt wird und keine statische Bitmap über das Video gemischt wird. Weitere Informationen finden Sie unter FENSTERLOSER VMR-Modus. Sie würden diese Funktion wie folgt aufrufen:

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

Positionieren des Videos

Nach dem Konfigurieren der VMR besteht der nächste Schritt im Festlegen der Position des Videos. Es sind zwei Rechtecke zu berücksichtigen: das Quellrechteck und das Zielrechteck. Das Quellrechteck definiert, welcher Teil des Videos angezeigt werden soll. Das Zielrechteck gibt den Bereich im Clientbereich des Fensters an, der das Video enthalten soll. Der VMR schneidet das Videobild auf das Quellrechteck zu und streckt das zugeschnittene Bild so, dass es an das Zielrechteck passt.

VMR-7

  1. Rufen Sie die IVMRWindowlessControl::SetVideoPosition-Methode auf, um beide Rechtecke anzugeben.
  2. Das Quellrechteck muss gleich oder kleiner als die größe des nativen Videos sein. Sie können die IVMRWindowlessControl::GetNativeVideoSize-Methode verwenden, um die native Videogröße zu erhalten.

VMR-9

  1. Rufen Sie die IVMRWindowlessControl9::SetVideoPosition-Methode auf, um beide Rechtecke anzugeben.
  2. Das Quellrechteck muss gleich oder kleiner als die größe des nativen Videos sein. Sie können die IVMRWindowlessControl9::GetNativeVideoSize-Methode verwenden, um die native Videogröße zu erhalten.

Der folgende Code legt beispielsweise die Quell- und Zielrechtecke für VMR-7 fest. Sie legt das Quellrechteck auf das gesamte Videobild und das Zielrechteck auf den gesamten Fensterclientbereich fest:

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

Wenn Sie ein Video erstellen möchten, um einen kleineren Teil des Clientbereichs zu belegen, ändern Sie den Parameter rcDest. Wenn Sie das Videobild zuschneiden möchten, ändern Sie den rcSrc-Parameter.

Behandeln von Fenstermeldungen

Da die VMR über kein eigenes Fenster verfügt, muss sie benachrichtigt werden, wenn das Video neu gepaint oder dessen Größe geändert werden muss. Reagieren Sie auf die folgenden Fenstermeldungen, indem Sie die aufgeführten VMR-Methoden aufrufen.

VMR-7

  1. WM _ PAINT. Rufen Sie IVMRWindowlessControl::RepaintVideo auf. Diese Methode bewirkt, dass VMR-7 den neuesten Videoframe neu anpaint.
  2. WM _ DISPLAYCHANGE:Rufen Sie IVMRWindowlessControl::D isplayModeChanged auf. Diese Methode benachrichtigt VMR-7, dass das Video mit einer neuen Auflösung oder Farbtiefe angezeigt werden muss.
  3. WM _ SIZE oder WM _ WINDOWPOSCHANGED:Berechnen Sie die Position des Videos neu, und rufen Sie IVMRWindowlessControl::SetVideoPosition auf, um die Position bei Bedarf zu aktualisieren.

VMR-9

  1. WM _ PAINT. Rufen Sie IVMRWindowlessControl9::RepaintVideo auf. Diese Methode bewirkt, dass VMR-9 den neuesten Videoframe neu anpaint.
  2. WM _ DISPLAYCHANGE:Rufen Sie IVMRWindowlessControl9::D isplayModeChanged auf. Diese Methode benachrichtigt VMR-9, dass das Video mit einer neuen Auflösung oder Farbtiefe angezeigt werden muss.
  3. WM _ SIZE oder WM _ WINDOWPOSCHANGED:Berechnen Sie die Position des Videos neu, und rufen Sie IVMRWindowlessControl9::SetVideoPosition auf, um die Position bei Bedarf zu aktualisieren.

Hinweis

Der Standardhandler für die WM _ WINDOWPOSCHANGED-Nachricht sendet eine WM _ SIZE-Nachricht. Wenn Ihre Anwendung WM _ WINDOWPOSCHANGED abfing und nicht an DefWindowProcübersendet, sollten Sie zusätzlich zum WM _ SIZE-Handler SetVideoPosition in Ihrem WM _ WINDOWPOSCHANGED-Handler aufrufen.

Das folgende Beispiel zeigt einen WM _ PAINT-Meldungshandler. Es zeichnet einen Bereich, der durch das Clientrechteck abzüglich des Videorechtecks definiert wird. Zeichnen Sie nicht auf das Videorechteck, da der VMR darüber zeichnet, was zu Flackern führt. Legen Sie aus demselben Grund keinen Hintergrundpinsel in Der Fensterklasse fest.

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

Obwohl Sie auf WM _ PAINT-Nachrichten reagieren müssen, müssen Sie zwischen WM _ PAINT-Nachrichten nichts tun, um das Video zu aktualisieren. Wie dieses Beispiel zeigt, können Sie das Videobild im fensterlosen Modus einfach als selbstzeichnenden Bereich im Fenster behandeln.

Verwenden des Renderers für das Mischen von Videos

Videorendering