Condividi tramite


Passaggio 4: Aggiungere il renderer video

[La funzionalità associata a questa pagina, DirectShow, è una funzionalità legacy. È stata sostituita da MediaPlayer, FMMediaEngine e Audio/Video Capture in Media Foundation. Queste funzionalità sono state ottimizzate per Windows 10 e Windows 11. Microsoft consiglia vivamente che il nuovo codice usi MediaPlayer, FMMediaEngine e Audio/Video Capture in Media Foundation anziché DirectShow, quando possibile. Microsoft suggerisce che il codice esistente che usa le API legacy venga riscritto per usare le nuove API, se possibile.

Questo argomento è il passaggio 4 dell'esercitazione Riproduzione audio/video in DirectShow. Il codice completo viene visualizzato nell'argomento DirectShow Playback Example.

DirectShow offre diversi filtri che eseguono il rendering del video:

Per altre informazioni sulle differenze tra questi filtri, vedere Scelta del renderer video corretto.

In questa esercitazione ogni filtro del renderer video viene sottoposto a wrapping da una classe che astrae alcune delle differenze tra di esse. Queste classi derivano da una classe base astratta denominata CVideoRenderer. La dichiarazione di CVideoRenderer è illustrata nel passaggio 2: Dichiarare CVideoRenderer e classi derivate.

Il metodo seguente tenta di creare a sua volta ogni renderer video, a partire da EVR, quindi vmR-9 e infine 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;
}

Filtro EVR

Il codice seguente crea il filtro EVR e lo aggiunge al grafico del filtro. La funzione AddFilterByCLSID usata in questo esempio viene visualizzata nell'argomento Aggiungi un filtro da CLSID.

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

La InitializeEVR funzione inizializza il filtro EVR. Questa funzione esegue i passaggi seguenti.

  1. Esegue query sul filtro per l'interfaccia IMFGetService .
  2. Chiama FMIGetService::GetService per ottenere un puntatore all'interfaccia FMVideoDisplayControl .
  3. Chiama FMIVideoDisplayControl::SetVideoWindow per impostare la finestra video.
  4. Chiama IMFVideoDisplayControl::SetAspectRatioMode per configurare EVR per mantenere la proporzioni video.

Il codice seguente mostra la InitializeEVR funzione.

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

Dopo la compilazione del grafico, le DShowPlayer::RenderStreams chiamate CVideoRenderer::FinalizeGraph. Questo metodo esegue qualsiasi inizializzazione finale o pulizia. Il codice seguente mostra l'implementazione CEVR di questo metodo.

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

Se L'EVR non è connesso ad alcun altro filtro, questo metodo rimuove l'EVR dal grafico. Ciò può verificarsi se il file multimediale non contiene un flusso video.

Filtro VMR-9

Il codice seguente crea il filtro VMR-9 e lo aggiunge al grafico del filtro.

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

La InitWindowlessVMR9 funzione inizializza vmR-9 per la modalità senza finestra. Per altre informazioni sulla modalità senza finestra, vedere Modalità senza finestra vmR. Questa funzione esegue i passaggi seguenti.

  1. Esegue query sul filtro VMR-9 per l'interfaccia IVMRFilterConfig9 .
  2. Chiama il metodo IVMRFilterConfig9::SetRenderingMode per impostare la modalità senza finestra.
  3. Esegue query sul filtro VMR-9 per l'interfaccia IVMRWindowlessControl9 .
  4. Chiama il metodo IVMRWindowlessControl9::SetVideoClippingWindow per impostare la finestra video.
  5. Chiama il metodo IVMRWindowlessControl9::SetAspectRatioMode per mantenere la proporzioni video.

Il codice seguente mostra la InitWindowlessVMR9 funzione.

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

Il CVMR9::FinalizeGraph metodo controlla se il filtro VMR-9 è connesso e, in caso contrario, lo rimuove dal grafico del filtro.

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

Filtro VMR-7

I passaggi per il filtro VMR-7 sono quasi identici a quelli per VMR-9, ad eccezione delle interfacce VMR-7. Il codice seguente crea il filtro VMR-7 e lo aggiunge al grafico del filtro.

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

La InitWindowlessVMR funzione inizializza vmR-7 per la modalità senza finestra. Questa funzione esegue i passaggi seguenti.

  1. Esegue query sul filtro VMR-7 per l'interfaccia IVMRFilterConfig .
  2. Chiama il metodo IVMRFilterConfig::SetRenderingMode per impostare la modalità senza finestre.
  3. Esegue query sul filtro VMR-7 per l'interfaccia IVMRWindowlessControl .
  4. Chiama il metodo IVMRWindowlessControl::SetVideoClippingWindow per impostare la finestra video.
  5. Chiama il metodo IVMRWindowlessControl::SetAspectRatioMode per mantenere la proporzioni video.

Il codice seguente mostra la InitWindowlessVMR funzione.

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

Il CVMR7::FinalizeGraph metodo controlla se il filtro VMR-7 è connesso e, in caso contrario, lo rimuove dal grafico del filtro.

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

Esempio di riproduzione DirectShow

Uso del filtro EVR DirectShow

Uso del renderer di mix video