Freigeben über


Schritt 4: Hinzufügen des Videorenderers

[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.]

Dieses Thema ist Schritt 4 des Tutorials Audio-/Videowiedergabe in DirectShow. Der vollständige Code wird im Thema DirectShow-Wiedergabebeispiel angezeigt.

DirectShow bietet verschiedene Filter zum Rendern von Videos:

Weitere Informationen zu den Unterschieden zwischen diesen Filtern finden Sie unter Auswählen des richtigen Videorenderers.

In diesem Tutorial wird jeder Videorendererfilter von einer Klasse umschlossen, die einige der Unterschiede zwischen ihnen abstrahiert. Diese Klassen leiten alle von einer abstrakten Basisklasse mit dem Namen ab CVideoRenderer. Die Deklaration von wird in Schritt 2: Deklarieren von CVideoRenderer und abgeleiteten Klassen gezeigt.CVideoRenderer

Die folgende Methode versucht, jeden Videorenderer nacheinander zu erstellen, beginnend mit dem EVR, dann dem VMR-9 und schließlich dem VMR-7.

HRESULT DShowPlayer::CreateVideoRenderer()
{
    HRESULT hr = E_FAIL;

    enum { Try_EVR, Try_VMR9, Try_VMR7 };

    for (DWORD i = Try_EVR; i <= Try_VMR7; i++)
    {
        switch (i)
        {
        case Try_EVR:
            m_pVideo = new (std::nothrow) CEVR();
            break;

        case Try_VMR9:
            m_pVideo = new (std::nothrow) CVMR9();
            break;

        case Try_VMR7:
            m_pVideo = new (std::nothrow) CVMR7();
            break;
        }

        if (m_pVideo == NULL)
        {
            hr = E_OUTOFMEMORY;
            break;
        }

        hr = m_pVideo->AddToGraph(m_pGraph, m_hwnd);
        if (SUCCEEDED(hr))
        {
            break;
        }

        delete m_pVideo;
        m_pVideo = NULL;
    }
    return hr;
}

EVR-Filter

Der folgende Code erstellt den EVR-Filter und fügt ihn dem Filterdiagramm hinzu. Die in diesem Beispiel verwendete Funktion AddFilterByCLSID wird im Thema Add a Filter by CLSID gezeigt.

HRESULT CEVR::AddToGraph(IGraphBuilder *pGraph, HWND hwnd)
{
    IBaseFilter *pEVR = NULL;

    HRESULT hr = AddFilterByCLSID(pGraph, CLSID_EnhancedVideoRenderer, 
        &pEVR, L"EVR");

    if (FAILED(hr))
    {
        goto done;
    }

    hr = InitializeEVR(pEVR, hwnd, &m_pVideoDisplay);
    if (FAILED(hr))
    {
        goto done;
    }

    // Note: Because IMFVideoDisplayControl is a service interface,
    // you cannot QI the pointer to get back the IBaseFilter pointer.
    // Therefore, we need to cache the IBaseFilter pointer.

    m_pEVR = pEVR;
    m_pEVR->AddRef();

done:
    SafeRelease(&pEVR);
    return hr;
}

Die InitializeEVR Funktion initialisiert den EVR-Filter. Diese Funktion führt die folgenden Schritte aus.

  1. Fragt den Filter für die IMFGetService-Schnittstelle ab.
  2. Ruft IMFGetService::GetService auf, um einen Zeiger auf die IMFVideoDisplayControl-Schnittstelle abzurufen.
  3. Ruft IMFVideoDisplayControl::SetVideoWindow auf, um das Videofenster festzulegen.
  4. Ruft IMFVideoDisplayControl::SetAspectRatioMode auf, um den EVR so zu konfigurieren, dass das Videoseitenverhältnis beibehalten wird.

Der folgende Code zeigt die InitializeEVR Funktion.

HRESULT InitializeEVR( 
    IBaseFilter *pEVR,              // Pointer to the EVR
    HWND hwnd,                      // Clipping window
    IMFVideoDisplayControl** ppDisplayControl
    ) 
{ 
    IMFGetService *pGS = NULL;
    IMFVideoDisplayControl *pDisplay = NULL;

    HRESULT hr = pEVR->QueryInterface(IID_PPV_ARGS(&pGS)); 
    if (FAILED(hr))
    {
        goto done;
    }

    hr = pGS->GetService(MR_VIDEO_RENDER_SERVICE, IID_PPV_ARGS(&pDisplay));
    if (FAILED(hr))
    {
        goto done;
    }

    // Set the clipping window.
    hr = pDisplay->SetVideoWindow(hwnd);
    if (FAILED(hr))
    {
        goto done;
    }

    // Preserve aspect ratio by letter-boxing
    hr = pDisplay->SetAspectRatioMode(MFVideoARMode_PreservePicture);
    if (FAILED(hr))
    {
        goto done;
    }

    // Return the IMFVideoDisplayControl pointer to the caller.
    *ppDisplayControl = pDisplay;
    (*ppDisplayControl)->AddRef();

done:
    SafeRelease(&pGS);
    SafeRelease(&pDisplay);
    return hr; 
} 

Nachdem das Diagramm erstellt wurde, ruft der DShowPlayer::RenderStreams auf CVideoRenderer::FinalizeGraph. Diese Methode führt eine endgültige Initialisierung oder Bereinigung durch. Der folgende Code zeigt die CEVR Implementierung dieser Methode.

HRESULT CEVR::FinalizeGraph(IGraphBuilder *pGraph)
{
    if (m_pEVR == NULL)
    {
        return S_OK;
    }

    BOOL bRemoved;
    HRESULT hr = RemoveUnconnectedRenderer(pGraph, m_pEVR, &bRemoved);
    if (bRemoved)
    {
        SafeRelease(&m_pEVR);
        SafeRelease(&m_pVideoDisplay);
    }
    return hr;
}

Wenn der EVR mit keinem anderen Filter verbunden ist, entfernt diese Methode den EVR aus dem Diagramm. Dies kann auftreten, wenn die Mediendatei keinen Videostream enthält.

VMR-9-Filter

Mit dem folgenden Code wird der VMR-9-Filter erstellt und dem Filterdiagramm hinzugefügt.

HRESULT CVMR9::AddToGraph(IGraphBuilder *pGraph, HWND hwnd)
{
    IBaseFilter *pVMR = NULL;

    HRESULT hr = AddFilterByCLSID(pGraph, CLSID_VideoMixingRenderer9, 
        &pVMR, L"VMR-9");
    if (SUCCEEDED(hr))
    {
        // Set windowless mode on the VMR. This must be done before the VMR 
        // is connected.
        hr = InitWindowlessVMR9(pVMR, hwnd, &m_pWindowless);
    }
    SafeRelease(&pVMR);
    return hr;
}

Die InitWindowlessVMR9 Funktion initialisiert VMR-9 für den fensterlosen Modus. (Weitere Informationen zum fensterlosen Modus finden Sie unter VMR-Fensterloser Modus.) Diese Funktion führt die folgenden Schritte aus.

  1. Fragt den VMR-9-Filter für die IVMRFilterConfig9-Schnittstelle ab.
  2. Ruft die IVMRFilterConfig9::SetRenderingMode-Methode auf, um den fensterlosen Modus festzulegen.
  3. Fragt den VMR-9-Filter für die IVMRWindowlessControl9-Schnittstelle ab.
  4. Ruft die IVMRWindowlessControl9::SetVideoClippingWindow-Methode auf, um das Videofenster festzulegen.
  5. Ruft die IVMRWindowlessControl9::SetAspectRatioMode-Methode auf, um das Videoseitenverhältnis beizubehalten.

Der folgende Code zeigt die InitWindowlessVMR9 Funktion.

HRESULT InitWindowlessVMR9( 
    IBaseFilter *pVMR,              // Pointer to the VMR
    HWND hwnd,                      // Clipping window
    IVMRWindowlessControl9** ppWC   // Receives a pointer to the VMR.
    ) 
{ 

    IVMRFilterConfig9 * pConfig = NULL; 
    IVMRWindowlessControl9 *pWC = NULL;

    // Set the rendering mode.  
    HRESULT hr = pVMR->QueryInterface(IID_PPV_ARGS(&pConfig)); 
    if (FAILED(hr))
    {
        goto done;
    }

    hr = pConfig->SetRenderingMode(VMR9Mode_Windowless); 
    if (FAILED(hr))
    {
        goto done;
    }

    // Query for the windowless control interface.
    hr = pVMR->QueryInterface(IID_PPV_ARGS(&pWC));
    if (FAILED(hr))
    {
        goto done;
    }

    // Set the clipping window.
    hr = pWC->SetVideoClippingWindow(hwnd);
    if (FAILED(hr))
    {
        goto done;
    }

    // Preserve aspect ratio by letter-boxing
    hr = pWC->SetAspectRatioMode(VMR9ARMode_LetterBox);
    if (FAILED(hr))
    {
        goto done;
    }

    // Return the IVMRWindowlessControl pointer to the caller.
    *ppWC = pWC;
    (*ppWC)->AddRef();

done:
    SafeRelease(&pConfig);
    SafeRelease(&pWC);
    return hr; 
} 

Die CVMR9::FinalizeGraph -Methode überprüft, ob der VMR-9-Filter verbunden ist, und entfernt ihn aus dem Filterdiagramm.

HRESULT CVMR9::FinalizeGraph(IGraphBuilder *pGraph)
{
    if (m_pWindowless == NULL)
    {
        return S_OK;
    }

    IBaseFilter *pFilter = NULL;

    HRESULT hr = m_pWindowless->QueryInterface(IID_PPV_ARGS(&pFilter));
    if (FAILED(hr))
    {
        goto done;
    }

    BOOL bRemoved;
    hr = RemoveUnconnectedRenderer(pGraph, pFilter, &bRemoved);

    // If we removed the VMR, then we also need to release our 
    // pointer to the VMR's windowless control interface.
    if (bRemoved)
    {
        SafeRelease(&m_pWindowless);
    }

done:
    SafeRelease(&pFilter);
    return hr;
}

VMR-7-Filter

Die Schritte für den VMR-7-Filter sind nahezu identisch mit denen für vmR-9, mit der Ausnahme, dass stattdessen die VMR-7-Schnittstellen verwendet werden. Der folgende Code erstellt den VMR-7-Filter und fügt ihn dem Filterdiagramm hinzu.

HRESULT CVMR7::AddToGraph(IGraphBuilder *pGraph, HWND hwnd)
{
    IBaseFilter *pVMR = NULL;

    HRESULT hr = AddFilterByCLSID(pGraph, CLSID_VideoMixingRenderer, 
        &pVMR, L"VMR-7");

    if (SUCCEEDED(hr))
    {
        // Set windowless mode on the VMR. This must be done before the VMR
        // is connected.
        hr = InitWindowlessVMR(pVMR, hwnd, &m_pWindowless);
    }
    SafeRelease(&pVMR);
    return hr;
}

Die InitWindowlessVMR Funktion initialisiert VMR-7 für den fensterlosen Modus. Diese Funktion führt die folgenden Schritte aus.

  1. Fragt den VMR-7-Filter für die IVMRFilterConfig-Schnittstelle ab.
  2. Ruft die IVMRFilterConfig::SetRenderingMode-Methode auf, um den fensterlosen Modus festzulegen.
  3. Fragt den VMR-7-Filter für die IVMRWindowlessControl-Schnittstelle ab.
  4. Ruft die IVMRWindowlessControl::SetVideoClippingWindow-Methode auf, um das Videofenster festzulegen.
  5. Ruft die IVMRWindowlessControl::SetAspectRatioMode-Methode auf, um das Videoseitenverhältnis beizubehalten.

Der folgende Code zeigt die InitWindowlessVMR Funktion.

HRESULT InitWindowlessVMR( 
    IBaseFilter *pVMR,              // Pointer to the VMR
    HWND hwnd,                      // Clipping window
    IVMRWindowlessControl** ppWC    // Receives a pointer to the VMR.
    ) 
{ 

    IVMRFilterConfig* pConfig = NULL; 
    IVMRWindowlessControl *pWC = NULL;

    // Set the rendering mode.  
    HRESULT hr = pVMR->QueryInterface(IID_PPV_ARGS(&pConfig)); 
    if (FAILED(hr))
    {
        goto done;
    }

    hr = pConfig->SetRenderingMode(VMRMode_Windowless); 
    if (FAILED(hr))
    {
        goto done;
    }

    // Query for the windowless control interface.
    hr = pVMR->QueryInterface(IID_PPV_ARGS(&pWC));
    if (FAILED(hr))
    {
        goto done;
    }

    // Set the clipping window.
    hr = pWC->SetVideoClippingWindow(hwnd);
    if (FAILED(hr))
    {
        goto done;
    }

    // Preserve aspect ratio by letter-boxing
    hr = pWC->SetAspectRatioMode(VMR_ARMODE_LETTER_BOX);
    if (FAILED(hr))
    {
        goto done;
    }

    // Return the IVMRWindowlessControl pointer to the caller.
    *ppWC = pWC;
    (*ppWC)->AddRef();

done:
    SafeRelease(&pConfig);
    SafeRelease(&pWC);
    return hr; 
} 

Die CVMR7::FinalizeGraph -Methode überprüft, ob der VMR-7-Filter verbunden ist, und entfernt ihn aus dem Filterdiagramm.

HRESULT CVMR7::FinalizeGraph(IGraphBuilder *pGraph)
{
    if (m_pWindowless == NULL)
    {
        return S_OK;
    }

    IBaseFilter *pFilter = NULL;

    HRESULT hr = m_pWindowless->QueryInterface(IID_PPV_ARGS(&pFilter));
    if (FAILED(hr))
    {
        goto done;
    }

    BOOL bRemoved;
    hr = RemoveUnconnectedRenderer(pGraph, pFilter, &bRemoved);

    // If we removed the VMR, then we also need to release our 
    // pointer to the VMR's windowless control interface.
    if (bRemoved)
    {
        SafeRelease(&m_pWindowless);
    }

done:
    SafeRelease(&pFilter);
    return hr;
}

DirectShow-Wiedergabebeispiel

Verwenden des DirectShow EVR-Filters

Verwenden des Videomischrenderers