Partager via


Étape 3 : Générer le graphe de filtre

[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 3 du tutoriel Lecture audio/vidéo dans DirectShow. Le code complet est illustré dans la rubrique Exemple de lecture DirectShow.

L’étape suivante consiste à générer un graphe de filtre pour lire le fichier multimédia.

Ouverture d’un fichier multimédia

La DShowPlayer::OpenFile méthode ouvre un fichier multimédia pour la lecture. Cette méthode effectue les opérations suivantes :

  1. Crée un graphe de filtre (vide).
  2. Appelle IGraphBuilder::AddSourceFilter pour ajouter un filtre source pour le fichier spécifié.
  3. Restitue les flux sur le filtre source.
HRESULT DShowPlayer::OpenFile(PCWSTR pszFileName)
{
    IBaseFilter *pSource = NULL;

    // Create a new filter graph. (This also closes the old one, if any.)
    HRESULT hr = InitializeGraph();
    if (FAILED(hr))
    {
        goto done;
    }
    
    // Add the source filter to the graph.
    hr = m_pGraph->AddSourceFilter(pszFileName, NULL, &pSource);
    if (FAILED(hr))
    {
        goto done;
    }

    // Try to render the streams.
    hr = RenderStreams(pSource);

done:
    if (FAILED(hr))
    {
        TearDownGraph();
    }
    SafeRelease(&pSource);
    return hr;
}

Création du Gestionnaire de graphe de filtre

La DShowPlayer::InitializeGraph méthode crée un nouveau graphe de filtre. Cette méthode effectue les opérations suivantes :

  1. Appelle CoCreateInstance pour créer un instance du Gestionnaire de graphes de filtre.
  2. Interroge le Gestionnaire de graphes de filtre pour les interfaces IMediaControl et IMediaEventEx .
  3. Appelle IMediaEventEx::SetNotifyWindow pour configurer la notification d’événement. Pour plus d’informations, consultez Notification d’événement dans DirectShow.
HRESULT DShowPlayer::InitializeGraph()
{
    TearDownGraph();

    // Create the Filter Graph Manager.
    HRESULT hr = CoCreateInstance(CLSID_FilterGraph, NULL, 
        CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&m_pGraph));
    if (FAILED(hr))
    {
        goto done;
    }

    hr = m_pGraph->QueryInterface(IID_PPV_ARGS(&m_pControl));
    if (FAILED(hr))
    {
        goto done;
    }

    hr = m_pGraph->QueryInterface(IID_PPV_ARGS(&m_pEvent));
    if (FAILED(hr))
    {
        goto done;
    }

    // Set up event notification.
    hr = m_pEvent->SetNotifyWindow((OAHWND)m_hwnd, WM_GRAPH_EVENT, NULL);
    if (FAILED(hr))
    {
        goto done;
    }

    m_state = STATE_STOPPED;

done:
    return hr;
}

Rendu des flux

L’étape suivante consiste à connecter le filtre source à un ou plusieurs filtres de convertisseur.

La DShowPlayer::RenderStreams méthode effectue les étapes suivantes.

  1. Interroge le Gestionnaire de graphes de filtre pour l’interface IFilterGraph2 .
  2. Ajoute un filtre de convertisseur vidéo au graphique de filtre.
  3. Ajoute le filtre De rendu DirectSound au graphe de filtre pour prendre en charge la lecture audio. Pour plus d’informations sur l’ajout de filtres au graphe de filtres, consultez Ajouter un filtre par CLSID.
  4. Énumère les broches de sortie sur le filtre source. Pour plus d’informations sur l’énumération des broches, consultez Énumération des épingles.
  5. Pour chaque broche, appelle la méthode IFilterGraph2::RenderEx . Cette méthode connecte la broche de sortie à un filtre de convertisseur, en ajoutant des filtres intermédiaires si nécessaire (par exemple, des décodeurs).
  6. Appelle CVideoRenderer::FinalizeGraph pour terminer l’initialisation du convertisseur vidéo.
  7. Supprime le filtre DirectSound Renderer si ce filtre n’est pas connecté à un autre filtre. Cela peut se produire si le fichier source ne contient pas de flux audio.
// Render the streams from a source filter. 

HRESULT DShowPlayer::RenderStreams(IBaseFilter *pSource)
{
    BOOL bRenderedAnyPin = FALSE;

    IFilterGraph2 *pGraph2 = NULL;
    IEnumPins *pEnum = NULL;
    IBaseFilter *pAudioRenderer = NULL;
    HRESULT hr = m_pGraph->QueryInterface(IID_PPV_ARGS(&pGraph2));
    if (FAILED(hr))
    {
        goto done;
    }

    // Add the video renderer to the graph
    hr = CreateVideoRenderer();
    if (FAILED(hr))
    {
        goto done;
    }

    // Add the DSound Renderer to the graph.
    hr = AddFilterByCLSID(m_pGraph, CLSID_DSoundRender, 
        &pAudioRenderer, L"Audio Renderer");
    if (FAILED(hr))
    {
        goto done;
    }

    // Enumerate the pins on the source filter.
    hr = pSource->EnumPins(&pEnum);
    if (FAILED(hr))
    {
        goto done;
    }

    // Loop through all the pins
    IPin *pPin;
    while (S_OK == pEnum->Next(1, &pPin, NULL))
    {           
        // Try to render this pin. 
        // It's OK if we fail some pins, if at least one pin renders.
        HRESULT hr2 = pGraph2->RenderEx(pPin, AM_RENDEREX_RENDERTOEXISTINGRENDERERS, NULL);

        pPin->Release();
        if (SUCCEEDED(hr2))
        {
            bRenderedAnyPin = TRUE;
        }
    }

    hr = m_pVideo->FinalizeGraph(m_pGraph);
    if (FAILED(hr))
    {
        goto done;
    }

    // Remove the audio renderer, if not used.
    BOOL bRemoved;
    hr = RemoveUnconnectedRenderer(m_pGraph, pAudioRenderer, &bRemoved);

done:
    SafeRelease(&pEnum);
    SafeRelease(&pAudioRenderer);
    SafeRelease(&pGraph2);

    // If we succeeded to this point, make sure we rendered at least one 
    // stream.
    if (SUCCEEDED(hr))
    {
        if (!bRenderedAnyPin)
        {
            hr = VFW_E_CANNOT_RENDER;
        }
    }
    return hr;
}

Voici le code de la RemoveUnconnectedRenderer fonction, qui est utilisé dans l’exemple précédent.

HRESULT RemoveUnconnectedRenderer(IGraphBuilder *pGraph, IBaseFilter *pRenderer, BOOL *pbRemoved)
{
    IPin *pPin = NULL;

    *pbRemoved = FALSE;

    // Look for a connected input pin on the renderer.

    HRESULT hr = FindConnectedPin(pRenderer, PINDIR_INPUT, &pPin);
    SafeRelease(&pPin);

    // If this function succeeds, the renderer is connected, so we don't remove it.
    // If it fails, it means the renderer is not connected to anything, so
    // we remove it.

    if (FAILED(hr))
    {
        hr = pGraph->RemoveFilter(pRenderer);
        *pbRemoved = TRUE;
    }

    return hr;
}

Publication du graphe de filtre

Lorsque l’application se ferme, elle doit libérer le graphe de filtre, comme indiqué dans le code suivant.

void DShowPlayer::TearDownGraph()
{
    // Stop sending event messages
    if (m_pEvent)
    {
        m_pEvent->SetNotifyWindow((OAHWND)NULL, NULL, NULL);
    }

    SafeRelease(&m_pGraph);
    SafeRelease(&m_pControl);
    SafeRelease(&m_pEvent);

    delete m_pVideo;
    m_pVideo = NULL;

    m_state = STATE_NO_GRAPH;
}

Suivant : Étape 4 : Ajouter le convertisseur vidéo.

Lecture audio/vidéo dans DirectShow

Exemple de lecture DirectShow

Génération du graphe de filtres

Techniques de Graph-Building générales