Binding Output Nodes to Media Sinks

This topic describes how to initialize the output nodes in a topology, if you are using the topology loader outside of the Media Session. An output node initially contains one of the following:

In the latter case, the IMFActivate pointer must be converted to an IMFStreamSink pointer before the topology loader resolves the topology. In most scenarios, the process works as follows:

  1. The application queues a partial topology on the Media Session.
  2. For all output nodes, the Media Session converts IMFActivate pointers to IMFStreamSink pointers. This process is called binding the output node to a media sink.
  3. The Media Session sends the modified topology to the IMFTopoLoader::Load method.

However, if you are using the topology loader directly (outside of the media sesssion), your application must bind the output nodes prior to calling IMFTopoLoader::Load. To bind an output node, do the following:

  1. Call IMFTopologyNode::GetObject to get the node's object pointer.
  2. Query the object pointer for the IMFStreamSink interface. If this call succeeds, there is nothing more to do, so skip the remaining steps.
  3. If the previous step failed, query the object pointer for the IMFActivate interface.
  4. Create the media sink by calling IMFActivate::ActivateObject. Specify IID_IMFMediaSink to get a pointer to the media sink's IMFMediaSink interface.
  5. Query the node for the MF_TOPONODE_STREAMID attribute. The value of this attribute is the identifier of the stream sink for this node. If the MF_TOPONODE_STREAMID attribute is not set, the default stream identifier is zero.
  6. The appropriate stream sink might already exist on the media sink. To check, call IMFMediaSink::GetStreamSinkById on the media sink. If the stream sink exists, the method returns a pointer to its IMFStreamSink interface. If this call fails, try to add a new stream sink by calling IMFMediaSink::AddStreamSink. If both calls fail, it means the media sink does not support the requested stream identifier, and this topology node is not configured correctly. Return an error code and skip the next step.
  7. Call IMFTopologyNode::SetObject, passing in the IMFStreamSink pointer from the previous step. This call replaces the node's object pointer, so that the node contains a pointer to the stream sink, instead of a pointer to the activation object.

The following code shows how to bind an output node.

// BindOutputNode
// Sets the IMFStreamSink pointer on an output node.

HRESULT BindOutputNode(IMFTopologyNode *pNode)
{
    IUnknown *pNodeObject = NULL;
    IMFActivate *pActivate = NULL;
    IMFStreamSink *pStream = NULL;
    IMFMediaSink *pSink = NULL;

    // Get the node's object pointer.
    HRESULT hr = pNode->GetObject(&pNodeObject);
    if (FAILED(hr))
    {
        return hr;
    }

    // The object pointer should be one of the following:
    // 1. An activation object for the media sink.
    // 2. The stream sink.

    // If it's #2, then we're already done.

    // First, check if it's an activation object.
    hr = pNodeObject->QueryInterface(IID_PPV_ARGS(&pActivate));

    if (SUCCEEDED(hr))
    {
        DWORD dwStreamID = 0;

        // The object pointer is an activation object. 
        
        // Try to create the media sink.
        hr = pActivate->ActivateObject(IID_PPV_ARGS(&pSink));

        // Look up the stream ID. (Default to zero.)

        if (SUCCEEDED(hr))
        {
           dwStreamID = MFGetAttributeUINT32(pNode, MF_TOPONODE_STREAMID, 0);
        }

        // Now try to get or create the stream sink.

        // Check if the media sink already has a stream sink with the requested ID.

        if (SUCCEEDED(hr))
        {
            hr = pSink->GetStreamSinkById(dwStreamID, &pStream);
            if (FAILED(hr))
            {
                // Try to add a new stream sink.
                hr = pSink->AddStreamSink(dwStreamID, NULL, &pStream);
            }
        }

        // Replace the node's object pointer with the stream sink. 
        if (SUCCEEDED(hr))
        {
            hr = pNode->SetObject(pStream);
        }
    }
    else
    {
        // Not an activation object. Is it a stream sink?
        hr = pNodeObject->QueryInterface(IID_PPV_ARGS(&pStream));
    }

    SafeRelease(&pNodeObject);
    SafeRelease(&pActivate);
    SafeRelease(&pStream);
    SafeRelease(&pSink);
    return hr;
}

Note

This example uses the SafeRelease function to release interface pointers.

 

The next example shows how to bind all of the output nodes in a topology. This example uses the IMFTopology::GetOutputNodeCollection method to get a collection of output nodes from the topology. Then it calls the function shown in the previous example on each of those nodes in turn.

// BindOutputNodes
// Sets the IMFStreamSink pointers on all of the output nodes in a topology.

HRESULT BindOutputNodes(IMFTopology *pTopology)
{
    DWORD cNodes = 0;

    IMFCollection *pCol = NULL;
    IUnknown *pUnk = NULL;
    IMFTopologyNode *pNode = NULL;

    // Get the collection of output nodes. 
    HRESULT hr = pTopology->GetOutputNodeCollection(&pCol);
    
    // Enumerate all of the nodes in the collection.
    if (SUCCEEDED(hr))
    {
        hr = pCol->GetElementCount(&cNodes);
    }

    if (SUCCEEDED(hr))
    {
        for (DWORD i = 0; i < cNodes; i++)
        {
            hr = pCol->GetElement(i, &pUnk);

            if (FAILED(hr)) { break; }

            hr = pUnk->QueryInterface(IID_IMFTopologyNode, (void**)&pNode);

            if (FAILED(hr)) { break; }

            // Bind this node.
            hr = BindOutputNode(pNode);

            if (FAILED(hr)) { break; }
        }
    }

    SafeRelease(&pCol);
    SafeRelease(&pUnk);
    SafeRelease(&pNode);
    return hr;
}

Advanced Topology Building

Media Sinks

IMFTopoLoader