Partager via


Étape 4 : Ajouter le convertisseur vidéo

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

Cette rubrique est l’étape 4 du tutoriel Lecture audio/vidéo dans DirectShow. Le code complet est illustré dans la rubrique Exemple de lecture DirectShow.

DirectShow fournit plusieurs filtres différents qui restituent la vidéo :

Pour plus d’informations sur les différences entre ces filtres, consultez Choisir le bon convertisseur vidéo.

Dans ce didacticiel, chaque filtre de convertisseur vidéo est encapsulé par une classe qui extrait certaines des différences entre eux. Ces classes dérivent toutes d’une classe de base abstraite nommée CVideoRenderer. La déclaration de CVideoRenderer est indiquée à l’étape 2 : Déclarer les classes CVideoRenderer et dérivées.

La méthode suivante tente de créer à son tour chaque convertisseur vidéo, en commençant par l’EVR, puis le VMR-9 et enfin le 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;
}

Filtre EVR

Le code suivant crée le filtre EVR et l’ajoute au graphe de filtre. La fonction AddFilterByCLSID utilisée dans cet exemple est illustrée dans la rubrique Ajouter un filtre par 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 fonction initialise le filtre EVR. Cette fonction effectue les étapes suivantes.

  1. Interroge le filtre pour l’interface IMFGetService .
  2. Appelle IMFGetService::GetService pour obtenir un pointeur vers l’interface IMFVideoDisplayControl .
  3. Appelle IMFVideoDisplayControl::SetVideoWindow pour définir la fenêtre vidéo.
  4. Appelle IMFVideoDisplayControl::SetAspectRatioMode pour configurer l’EVR pour préserver le rapport d’aspect vidéo.

Le code suivant montre la InitializeEVR fonction .

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

Une fois le graphe généré, le DShowPlayer::RenderStreams appelle CVideoRenderer::FinalizeGraph. Cette méthode effectue toute initialisation ou nettoyage final. Le code suivant illustre l’implémentation CEVR de cette méthode.

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

Si l’EVR n’est connecté à aucun autre filtre, cette méthode supprime l’EVR du graphique. Cela peut se produire si le fichier multimédia ne contient pas de flux vidéo.

Filtre VMR-9

Le code suivant crée le filtre VMR-9 et l’ajoute au graphe de filtre.

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 fonction initialise le VMR-9 pour le mode sans fenêtre. (Pour plus d’informations sur le mode sans fenêtre, consultez Mode sans fenêtre VMR.) Cette fonction effectue les étapes suivantes.

  1. Interroge le filtre VMR-9 pour l’interface IVMRFilterConfig9 .
  2. Appelle la méthode IVMRFilterConfig9::SetRenderingMode pour définir le mode sans fenêtre.
  3. Interroge le filtre VMR-9 pour l’interface IVMRWindowlessControl9 .
  4. Appelle la méthode IVMRWindowlessControl9::SetVideoClippingWindow pour définir la fenêtre vidéo.
  5. Appelle la méthode IVMRWindowlessControl9::SetAspectRatioMode pour conserver le rapport d’aspect vidéo.

Le code suivant montre la InitWindowlessVMR9 fonction .

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

La CVMR9::FinalizeGraph méthode vérifie si le filtre VMR-9 est connecté et, si ce n’est pas le cas, le supprime du graphe de filtre.

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

Filtre VMR-7

Les étapes du filtre VMR-7 sont presque identiques à celles du VMR-9, sauf que les interfaces VMR-7 sont utilisées à la place. Le code suivant crée le filtre VMR-7 et l’ajoute au graphe de filtre.

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 fonction initialise le VMR-7 en mode sans fenêtre. Cette fonction effectue les étapes suivantes.

  1. Interroge le filtre VMR-7 pour l’interface IVMRFilterConfig .
  2. Appelle la méthode IVMRFilterConfig::SetRenderingMode pour définir le mode sans fenêtre.
  3. Interroge le filtre VMR-7 pour l’interface IVMRWindowlessControl .
  4. Appelle la méthode IVMRWindowlessControl::SetVideoClippingWindow pour définir la fenêtre vidéo.
  5. Appelle la méthode IVMRWindowlessControl::SetAspectRatioMode pour conserver le rapport d’aspect vidéo.

Le code suivant montre la InitWindowlessVMR fonction .

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

La CVMR7::FinalizeGraph méthode vérifie si le filtre VMR-7 est connecté et, dans le cas contraire, le supprime du graphique de filtre.

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

Exemple de lecture DirectShow

Utilisation du filtre EVR DirectShow

Utilisation du convertisseur de mixage vidéo