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, IMFMediaEngineet audio/vidéo capture dans 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 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.]

En fonction d’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, vérifiez si cette broche est connectée à une autre broche. Si c’est le cas, interrogez l’autre broche pour son propre filtre. Vous pouvez parcourir le graphique dans la direction en amont en énumérant les broches d’entrée du filtre ou dans la direction en aval en énumérant les broches de sortie.

La fonction suivante recherche en amont ou en aval un filtre connecté. Elle retourne le premier filtre correspondant 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, elle appelle IPin ::QueryDirection pour vérifier si la broche 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 broche. 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é à deux filtres ou plus dans les deux sens. Par exemple, il peut s’agir d’un filtre de fractionnement, avec plusieurs filtres en aval. Ou il peut s’agir d’un filtre mux, avec plusieurs filtres en amont. Par conséquent, vous souhaiterez peut-être les collecter dans une liste.

Le code suivant montre une façon 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. Par les règles de COM, deux pointeurs d’interface font référence au même objet si et uniquement s’ils retournent des pointeurs identiques IUnknown. 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 générales Graph-Building