Suchen eines Filterspeer

Bei einem Filter können Sie das Diagramm durchlaufen, indem Sie die Filter suchen, mit denen es verbunden ist. Beginnen Sie, indem Sie die Pins des Filters aufzählen. Überprüfen Sie für jeden Stecknadel, ob dieser Stecknadel mit einem anderen Pin verbunden ist. Wenn ja, fragen Sie den anderen Pin nach seinem besitzenden Filter ab. Sie können das Diagramm in Upstreamrichtung durch Aufzählen der Eingabepins des Filters oder in downstream-Richtung durch Aufzählen der Ausgabepins gehen.

Die folgende Funktion durchsucht upstream oder downstream nach einem verbundenen Filter. Es gibt den ersten übereinstimmenden Filter zurück, der gefunden wird:

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

Die Funktion ruft IBaseFilter::EnumPins auf, um die Pins des ersten Filters aufzuzählen. Für jede Stecknadel ruft sie IPin::QueryDirection auf, um zu überprüfen, ob der Stecknadel mit der angegebenen Richtung (Eingabe oder Ausgabe) übereinstimmt. In diesem Falle bestimmt die Funktion durch Aufrufen der IPin::ConnectedTo-Methode, ob dieser Pin mit einem anderen Pin verbunden ist. Schließlich ruft sie IPin::QueryPinInfo auf dem verbundenen Pin auf. Diese Methode gibt eine -Struktur zurück, die unter anderem einen Zeiger auf den besitzenden Filter dieses Pins enthält. Dieser Zeiger wird an den Aufrufer im ppNext-Parameter zurückgegeben. Der Aufrufer muss den Zeiger freigeben.

Der folgende Code zeigt, wie diese Funktion aufgerufen wird:

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

Ein Filter kann in beide Richtungen mit zwei oder mehr Filtern verbunden sein. Es kann sich z. B. um einen Splitterfilter mit mehreren Filtern nachgeschaltet haben. Oder es kann sich um einen Mux-Filter mit mehreren Upstreamfiltern aus diesem filtert. Aus diesem Grund sollten Sie alle in einer Liste erfassen.

Der folgende Code zeigt eine mögliche Möglichkeit, eine solche Funktion zu implementieren. Es wird die DirectShow CGenericList-Klasse verwendet. Sie könnten eine entsprechende Funktion mithilfe einer anderen Datenstruktur schreiben.

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

Um dies etwas zu erschweren, kann ein Filter mehrere Stecknadelverbindungen mit demselben Filter aufweisen. Um zu vermeiden, dass Duplikate in die Liste aufgenommen werden, fragen Sie jeden IBaseFilter-Zeiger nach IUnknown ab, und vergleichen Sie die IUnknown-Zeiger. Nach den Regeln von COM verweisen zwei Schnittstellenzeiger nur dann auf dasselbe Objekt, wenn sie identische IUnknown-Zeiger zurückgeben. Im vorherigen Beispiel behandelt die AddFilterUnique-Funktion dieses Detail.

Das folgende Beispiel zeigt die Verwendung der GetPeerFilters-Funktion:

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

Allgemeine Graph-Building Techniken