Step 4. Set Allocator Properties

[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 is step 4 of the tutorial Writing Transform Filters.

Note

This step is not required for filters that derive from CTransInPlaceFilter.

 

After two pins agree on a media type, they select an allocator for the connection and negotiate allocator properties, such as the buffer size and the number of buffers.

In the CTransformFilter class, there are two allocators, one for the upstream pin connection and one for the downstream pin connection. The upstream filter selects the upstream allocator and also chooses the allocator properties. The input pin accepts whatever the upstream filter decides. If you need to modify this behavior, override the CBaseInputPin::NotifyAllocator method.

The transform filter's output pin selects the downstream allocator. It performs the following steps:

  1. If the downstream filter can provide an allocator, the output pin uses that one. Otherwise, the output pin creates a new allocator.
  2. The output pin gets the downstream filter's allocator requirements (if any) by calling IMemInputPin::GetAllocatorRequirements.
  3. The output pin calls the transform filter's CTransformFilter::DecideBufferSize method, which is pure virtual. The parameters to this method are a pointer to the allocator and an ALLOCATOR_PROPERTIES structure with the downstream filter's requirements. If the downstream filter has no allocator requirements, the structure is zeroed out.
  4. In the DecideBufferSize method, the derived class sets the allocator properties by calling IMemAllocator::SetProperties.

Generally, the derived class will select allocator properties based on the output format, the downstream filter's requirements, and the filter's own requirements. Try to select properties that are compatible with the downstream filter's request. Otherwise, the downstream filter might reject the connection.

In the following example, the RLE encoder sets minimum values for the buffer size, buffer alignment, and buffer count. For the prefix, it uses whatever value the downstream filter requested. The prefix is typically zero bytes, but some filters require it. For example, the AVI Mux filter uses the prefix to write RIFF headers.

HRESULT CRleFilter::DecideBufferSize(
    IMemAllocator *pAlloc, ALLOCATOR_PROPERTIES *pProp)
{
    AM_MEDIA_TYPE mt;
    HRESULT hr = m_pOutput->ConnectionMediaType(&mt);
    if (FAILED(hr))
    {
        return hr;
    }

    ASSERT(mt.formattype == FORMAT_VideoInfo);
    BITMAPINFOHEADER *pbmi = HEADER(mt.pbFormat);
    pProp->cbBuffer = DIBSIZE(*pbmi) * 2; 
    if (pProp->cbAlign == 0)
    {
        pProp->cbAlign = 1;
    }
    if (pProp->cBuffers == 0)
    {
        pProp->cBuffers = 1;
    }
    // Release the format block.
    FreeMediaType(mt);

    // Set allocator properties.
    ALLOCATOR_PROPERTIES Actual;
    hr = pAlloc->SetProperties(pProp, &Actual);
    if (FAILED(hr)) 
    {
        return hr;
    }
    // Even when it succeeds, check the actual result.
    if (pProp->cbBuffer > Actual.cbBuffer) 
    {
        return E_FAIL;
    }
    return S_OK;
}

The allocator may not be able to match your request exactly. Therefore, the SetProperties method returns the actual result in a separate ALLOCATOR_PROPERTIES structure (the Actual parameter, in the previous example). Even when SetProperties succeeds, you should check the result to make sure they meet your filter's minimum requirements.

Custom Allocators

By default, all of the filter classes use the CMemAllocator class for their allocators. This class allocates memory from the virtual address space of the client process (using VirtualAlloc). If your filter needs to use some other kind of memory, such as DirectDraw surfaces, you can implement a custom allocator. You can use the CBaseAllocator class or write an entirely new allocator class. If your filter has a custom allocator, override the following methods, depending on which pin uses the allocator:

If the other filter refuses to connect using your custom allocator, your filter can either fail the connection, or else connect with the other filter's allocator. In the latter case, you might need to set an internal flag indicating the type of allocator. For an example of this approach, see CDrawImage Class.

Next: Step 5. Transform the Image.

Writing DirectShow Filters