Freigeben über


Verwenden des fensterlosen Modus

[Das dieser Seite zugeordnete Feature DirectShow ist ein Legacyfeature. Es wurde durch MediaPlayer, IMFMediaEngine und Audio/Video Capture in Media Foundation ersetzt. Diese Features wurden für Windows 10 und Windows 11 optimiert. Microsoft empfiehlt dringend, dass neuer Code nach Möglichkeit MediaPlayer, IMFMediaEngine und Audio/Video Capture in Media Foundation anstelle von DirectShow verwendet. Microsoft schlägt vor, vorhandenen Code, der die Legacy-APIs verwendet, um nach Möglichkeit die neuen APIs zu verwenden.]

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

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

  • Am wichtigsten ist, dass es ein Potenzial für Deadlocks gibt, wenn Fensternachrichten zwischen Threads gesendet werden.
  • Der Filter Graph-Manager muss bestimmte Fenstermeldungen, z. B. WM_PAINT, an den Videorenderer weiterleiten. Die Anwendung muss die Implementierung von IVideoWindow (und nicht die des Videorenderers) des Filter Graph-Managers verwenden, damit der Filter Graph-Manager den richtigen internen Zustand beibehält.
  • Zum Empfangen von Maus- oder Tastaturereignissen aus dem Videofenster muss die Anwendung einen Nachrichtenablauf festlegen, sodass das Videofenster diese Nachrichten an die Anwendung weiterleitet.
  • Um Abschneideprobleme zu vermeiden, muss das Videofenster über die richtigen Fensterstile verfügen.

Der fensterlose Modus vermeidet diese Probleme, indem die VMR direkt im Clientbereich des Anwendungsfensters zeichnen und DirectDraw zum Ausschneiden des Videorechtecks verwendet wird. Der fensterlose Modus verringert die Wahrscheinlichkeit von Deadlocks erheblich. Außerdem muss die Anwendung nicht das Besitzerfenster oder 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 den VMR explizit konfigurieren. Sie werden jedoch feststellen, dass das flexibler und einfacher zu verwenden ist als der Fenstermodus.

Der VMR-7-Filter und der VMR-9-Filter machen unterschiedliche Schnittstellen verfügbar, aber die Schritte sind jeweils gleichwertig.

Konfigurieren der VMR für den Fensterlosen Modus

Um das Standardverhalten der VMR zu überschreiben, konfigurieren Sie den VMR vor dem Erstellen des Filterdiagramms:

VMR-7

  1. Erstellen Sie den Filter graph-Manager.
  2. Erstellen Sie VMR-7, und fügen Sie sie dem Filterdiagramm hinzu.
  3. Rufen Sie IVMRFilterConfig::SetRenderingMode auf der VMR-7 mit dem flag VMRMode_Windowless auf.
  4. Fragen Sie VMR-7 nach der IVMRWindowlessControl-Schnittstelle ab.
  5. Rufen Sie IVMRWindowlessControl::SetVideoClippingWindow auf der 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 sie dem Filterdiagramm hinzu.
  3. Rufen Sie IVMRFilterConfig9::SetRenderingMode auf der VMR-9 mit dem flag VMR9Mode_Windowless auf.
  4. Fragen Sie VMR-9 für die IVMRWindowlessControl9-Schnittstelle ab.
  5. Rufen Sie IVMRWindowlessControl9::SetVideoClippingWindow auf der 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 Methoden zum Erstellen von Graphen aufrufen. Der Filter Graph Manager verwendet automatisch die instance der VMR, die Sie dem Diagramm hinzugefügt haben. (Ausführliche Informationen dazu, warum dies geschieht, finden Sie unter Intelligent Connect.)

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

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 dem Video gemischt wird. Ausführliche Informationen finden Sie unter VMR-Fensterloser 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 darin, die Position des Videos festzulegen. 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 die Region im Clientbereich des Fensters an, die das Video enthalten soll. Die VMR schneidet das Videobild auf das Quellrechteck zu und dehnt das zugeschnittene Bild so, dass es dem Zielrechteck entspricht.

VMR-7

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

VMR-9

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

Der folgende Code legt beispielsweise die Quell- und Zielrechtecke für die VMR-7 fest. Sie legt das Quellrechteck gleich dem gesamten Videobild und das Zielrechteck gleich dem 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 video einen kleineren Teil des Clientbereichs belegen möchten, ändern Sie den rcDest-Parameter . Wenn Sie das Videobild zuschneiden möchten, ändern Sie den rcSrc-Parameter .

Behandeln von Fenstermeldungen

Da die VMR kein eigenes Fenster hat, muss sie benachrichtigt werden, wenn das Video neu gezeichnet oder die Größe geändert werden muss. Antworten 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 streicht.
  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 streicht.
  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 jedoch WM_WINDOWPOSCHANGED abfängt und sie nicht an DefWindowProc übergibt, sollten Sie setVideoPosition in Ihrem WM_WINDOWPOSCHANGED-Handler zusätzlich zu Ihrem WM_SIZE-Handler aufrufen.

 

Das folgende Beispiel zeigt einen WM_PAINT Nachrichtenhandler. Es zeichnet einen Bereich, der vom Clientrechteck abzüglich des Videorechtecks definiert ist. Zeichnen Sie nicht auf das Videorechteck, da die VMR es übermalt und flackert. Legen Sie aus dem gleichen Grund keinen Hintergrundpinsel in Ihrer 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 im fensterlosen Modus das Videobild einfach als selbst zeichnenden Bereich im Fenster behandeln.

Verwenden des Videomischrenderers

Videorendering