Search code examples
c++comdirectshow

CBasePin incrementing reference to owning filter. Circular reference?


Finally I have gathered the courage to try to build a DirectShow filter for the first time. This is probably a dumb question, but I am very confused.

My filter is derived from CBaseFilter, and owns a single output pin, derived from CBaseInputPin. The output pin's member reference is being held by a CComPtr smart pointer. When the output pin gets constructed, I notice it stores a reference to the "owner" filter on a dumb pointer. Makes sense to me as we don't want to create a circular reference.

However, when the CComPtr member adds a reference to the input pin, the CBasePin::NonDelegatingAddRef() method is called.

Here's the source code for the method

/* Override to increment the owning filter's reference count */

    STDMETHODIMP_(ULONG)
    CBasePin::NonDelegatingAddRef()
    {
        ASSERT(InterlockedIncrement(&m_cRef) > 0);
        return m_pFilter->AddRef();
    }

I can't wrap up my mind as to why the child pin needs to increment the reference count of the owning filter (creating in effect a circular reference).

Should I hold the reference of the owned pin on a dumb pointer and delete it regardless of the reference count?


Solution

  • Filter and pins are COM objects but they don't have separate lifetime. They have just one reference count - filter's. Pin referencing and dereferencing effectively increment and decrement filter's counter and when it reaches zero filter and all its pins are being destroyed.

    External code deal with COM pointers the usual way. Internally, and many filter samples show you the way, you delete pins when you are done, e.g. (random pick):

    //
    // CSource::Destructor
    //
    
    CSource::~CSource()
    {
        /*  Free our pins and pin array */
        while (m_iPins != 0) {
        // deleting the pins causes them to be removed from the array...
            delete m_paStreams[m_iPins - 1];
        }
        ASSERT(m_paStreams == NULL);
    }
    

    In most cases pins are static and deleted in filter's destructor. When they are dynamic it is not safe to delete them on the go because their interface pointers might remain referenced with pending IUnknown::Release on the object you are to delete. However if you just keep it in a side list internally in the filter to postpone delete for the time of filter destruction, this is going to be safe.

    Also you are not supposed to have pins managed by CComPtr inside the filter. The intended way is shown in the same piece of code: new and delete for pin objects and direct raw pointers to pins in filter's code. External code communicates with pin via its COM interface pointers.