Providing a Custom Allocator

[The feature associated with this page, DirectShow, is a legacy feature. It has been superseded by MediaPlayer, IMFMediaEngine, and Audio/Video Capture in Media Foundation. Those features have been optimized for Windows 10 and Windows 11. Microsoft strongly recommends that new code use MediaPlayer, IMFMediaEngine and Audio/Video Capture in Media Foundation instead of DirectShow, when possible. Microsoft suggests that existing code that uses the legacy APIs be rewritten to use the new APIs if possible.]

This section describes how to provide a custom allocator for a filter. Only IMemInputPin connections are described, but the steps for an IAsyncReader connection are similar.

First, define a C++ class for the allocator. Your allocator can derive from one of the standard allocator classes, CBaseAllocator or CMemAllocator, or you can create an entirely new allocator class. If you create a new class, it must expose the IMemAllocator interface.

The remaining steps depend on whether your allocator belongs to an input pin or an output pin on your filter. Input pins play a different role than output pins during the allocator negotiation phase, because the output pin ultimately selects the allocator.

Providing a Custom Allocator for an Input Pin

To provide an allocator for an input pin, override the input pin's CBaseInputPin::GetAllocator method. Within this method, check the m_pAllocator member variable. If this variable is non-NULL, it means the allocator has already been selected for this connection, so the GetAllocator method must return a pointer to that allocator. If m_pAllocator is NULL, it means the allocator has not been selected, so the GetAllocator method should return a pointer to the input pin's preferred allocator. In that case, create an instance of your custom allocator and return its IMemAllocator pointer. The following code shows how to implement the GetAllocator method:

STDMETHODIMP CMyInputPin::GetAllocator(IMemAllocator **ppAllocator)
{
    CheckPointer(ppAllocator, E_POINTER);
    if (m_pAllocator)  
    {
        // We already have an allocator, so return that one.
        *ppAllocator = m_pAllocator;
        (*ppAllocator)->AddRef();
        return S_OK;
    }

    // No allocator yet, so propose our custom allocator. The exact code
    // here will depend on your custom allocator class definition.
    HRESULT hr = S_OK;
    CMyAllocator *pAlloc = new CMyAllocator(&hr);
    if (!pAlloc)
    {
        return E_OUTOFMEMORY;
    }
    if (FAILED(hr))
    {
        delete pAlloc;
        return hr;
    }

    // Return the IMemAllocator interface to the caller.
    return pAlloc->QueryInterface(IID_IMemAllocator, (void**)ppAllocator);
}

When the upstream filter selects an allocator, it calls the input pin's IMemInputPin::NotifyAllocator method. Override the CBaseInputPin::NotifyAllocator method to check the allocator properties. In some cases, the input pin might reject the allocator if it is not your custom allocator, although this may cause the entire pin connection to fail.

Providing a Custom Allocator for an Output Pin

To provide an allocator for an output pin, override the CBaseOutputPin::InitAllocator method to create an instance of your allocator:

HRESULT MyOutputPin::InitAllocator(IMemAllocator **ppAllocator)
{
    HRESULT hr = S_OK;
    CMyAllocator *pAlloc = new CMyAllocator(&hr);
    if (!pAlloc)
    {
        return E_OUTOFMEMORY;
    }

    if (FAILED(hr))
    {
        delete pAlloc;
        return hr;
    }

    // Return the IMemAllocator interface.
    return pAlloc->QueryInterface(IID_IMemAllocator, (void**)ppAllocator);
}

By default, the CBaseOutputPin class requests an allocator from the input pin first. If that allocator is not suitable, the output pin creates its own allocator. To force the connection to use your custom allocator, override the CBaseOutputPin::DecideAllocator method. However, be aware that this can prevent your output pin from connecting with certain filters, because the other filter may also require its own custom allocator. A third option is to switch the order: Try your custom allocator first, and then fall back to the other filter's allocator.

Negotiating Allocators