Partager via


Rechercher un homologue de filtres

[La fonctionnalité associée à cette page, DirectShow, est une fonctionnalité héritée. Il a été remplacé par MediaPlayer, IMFMediaEngine et Audio/Video Capture in Media Foundation. Ces fonctionnalités ont été optimisées pour Windows 10 et Windows 11. Microsoft recommande vivement que le nouveau code utilise MediaPlayer, IMFMediaEngine et Audio/Video Capture dans Media Foundation au lieu de DirectShow, si 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.]

Avec un filtre, vous pouvez parcourir le graphique en recherchant les filtres auxquels il est connecté. Commencez par énumérer les broches du filtre. Pour chaque broche, case activée si cette broche est connectée à une autre broche. Si c’est le cas, interrogez l’autre broche pour qu’elle soit propriétaire du filtre. Vous pouvez parcourir le graphique dans la direction amont en énumérant les broches d’entrée du filtre ou en aval en énumérant les broches de sortie.

La fonction suivante recherche un filtre connecté amont ou en aval. Il retourne le premier filtre de correspondance qu’il trouve :

// Get the first upstream or downstream filter
HRESULT GetNextFilter(
    IBaseFilter *pFilter, // Pointer to the starting filter
    PIN_DIRECTION Dir,    // Direction to search (upstream or downstream)
    IBaseFilter **ppNext) // Receives a pointer to the next filter.
{
    if (!pFilter || !ppNext) return E_POINTER;

    IEnumPins *pEnum = 0;
    IPin *pPin = 0;
    HRESULT hr = pFilter->EnumPins(&pEnum);
    if (FAILED(hr)) return hr;
    while (S_OK == pEnum->Next(1, &pPin, 0))
    {
        // See if this pin matches the specified direction.
        PIN_DIRECTION ThisPinDir;
        hr = pPin->QueryDirection(&ThisPinDir);
        if (FAILED(hr))
        {
            // Something strange happened.
            hr = E_UNEXPECTED;
            pPin->Release();
            break;
        }
        if (ThisPinDir == Dir)
        {
            // Check if the pin is connected to another pin.
            IPin *pPinNext = 0;
            hr = pPin->ConnectedTo(&pPinNext);
            if (SUCCEEDED(hr))
            {
                // Get the filter that owns that pin.
                PIN_INFO PinInfo;
                hr = pPinNext->QueryPinInfo(&PinInfo);
                pPinNext->Release();
                pPin->Release();
                pEnum->Release();
                if (FAILED(hr) || (PinInfo.pFilter == NULL))
                {
                    // Something strange happened.
                    return E_UNEXPECTED;
                }
                // This is the filter we're looking for.
                *ppNext = PinInfo.pFilter; // Client must release.
                return S_OK;
            }
        }
        pPin->Release();
    }
    pEnum->Release();
    // Did not find a matching filter.
    return E_FAIL;
}

La fonction appelle IBaseFilter::EnumPins pour énumérer les broches du premier filtre. Pour chaque broche, il appelle IPin::QueryDirection pour case activée si l’épingle correspond à la direction spécifiée (entrée ou sortie). Si c’est le cas, la fonction détermine si cette broche est connectée à une autre broche, en appelant la méthode IPin::ConnectedTo . Enfin, il appelle IPin::QueryPinInfo sur la broche connectée. Cette méthode retourne une structure qui contient, entre autres, un pointeur vers le filtre propriétaire de cette épingle. Ce pointeur est retourné à l’appelant dans le paramètre ppNext . L’appelant doit libérer le pointeur.

Le code suivant montre comment appeler cette fonction :

IBaseFilter *pF; // Pointer to some filter.
IBaseFilter *pUpstream = NULL;
if (SUCCEEDED(GetNextFilter(pF, PINDIR_INPUT, &pUpstream)))
{
    // Use pUpstream ...
    pUpstream->Release();
}

Un filtre peut être connecté à au moins deux filtres dans les deux sens. Par exemple, il peut s’agir d’un filtre de fractionnement, avec plusieurs filtres en aval. Il peut également s’agir d’un filtre mux, avec plusieurs filtres amont à partir de celui-ci. Par conséquent, vous souhaiterez peut-être les collecter dans une liste.

Le code suivant montre un moyen possible d’implémenter une telle fonction. Il utilise la classe DirectShow CGenericList ; vous pouvez écrire une fonction équivalente à l’aide d’une autre structure de données.

#include <streams.h>  // Link to the DirectShow base class library
// Define a typedef for a list of filters.
typedef CGenericList<IBaseFilter> CFilterList;

// Forward declaration. Adds a filter to the list unless it's a duplicate.
void AddFilterUnique(CFilterList &FilterList, IBaseFilter *pNew);

// Find all the immediate upstream or downstream peers of a filter.
HRESULT GetPeerFilters(
    IBaseFilter *pFilter, // Pointer to the starting filter
    PIN_DIRECTION Dir,    // Direction to search (upstream or downstream)
    CFilterList &FilterList)  // Collect the results in this list.
{
    if (!pFilter) return E_POINTER;

    IEnumPins *pEnum = 0;
    IPin *pPin = 0;
    HRESULT hr = pFilter->EnumPins(&pEnum);
    if (FAILED(hr)) return hr;
    while (S_OK == pEnum->Next(1, &pPin, 0))
    {
        // See if this pin matches the specified direction.
        PIN_DIRECTION ThisPinDir;
        hr = pPin->QueryDirection(&ThisPinDir);
        if (FAILED(hr))
        {
            // Something strange happened.
            hr = E_UNEXPECTED;
            pPin->Release();
            break;
        }
        if (ThisPinDir == Dir)
        {
            // Check if the pin is connected to another pin.
            IPin *pPinNext = 0;
            hr = pPin->ConnectedTo(&pPinNext);
            if (SUCCEEDED(hr))
            {
                // Get the filter that owns that pin.
                PIN_INFO PinInfo;
                hr = pPinNext->QueryPinInfo(&PinInfo);
                pPinNext->Release();
                if (FAILED(hr) || (PinInfo.pFilter == NULL))
                {
                    // Something strange happened.
                    pPin->Release();
                    pEnum->Release();
                    return E_UNEXPECTED;
                }
                // Insert the filter into the list.
                AddFilterUnique(FilterList, PinInfo.pFilter);
                PinInfo.pFilter->Release();
            }
        }
        pPin->Release();
    }
    pEnum->Release();
    return S_OK;
}
void AddFilterUnique(CFilterList &FilterList, IBaseFilter *pNew)
{
    if (pNew == NULL) return;

    POSITION pos = FilterList.GetHeadPosition();
    while (pos)
    {
        IBaseFilter *pF = FilterList.GetNext(pos);
        if (IsEqualObject(pF, pNew))
        {
            return;
        }
    }
    pNew->AddRef();  // The caller must release everything in the list.
    FilterList.AddTail(pNew);
}

Pour compliquer quelque peu les choses, un filtre peut avoir plusieurs connexions d’épingle au même filtre. Pour éviter de placer des doublons dans la liste, interrogez chaque pointeur IBaseFilter pour IUnknown et comparez les pointeurs IUnknown . Selon les règles de COM, deux pointeurs d’interface font référence au même objet si et seulement s’ils retournent des pointeurs IUnknown identiques. Dans l’exemple précédent, la fonction AddFilterUnique gère ce détail.

L’exemple suivant montre comment utiliser la fonction GetPeerFilters :

IBaseFilter *pF; // Pointer to some filter.
CFilterList FList(NAME("MyList"));  // List to hold the downstream peers.
hr = GetPeerFilters(pF, PINDIR_OUTPUT, FList);
if (SUCCEEDED(hr))
{
    POSITION pos = FList.GetHeadPosition();
    while (pos)
    {
        IBaseFilter *pDownstream = FList.GetNext(pos);
        pDownstream->Release();
    }
}

Techniques de Graph-Building générales