How to Create a Playlist

This topic describes how to use the Sequence Source to play a sequence of files.

Overview

To play media files in a sequence, the application must add topologies in a sequence to create a playlist, and queue these topologies on the Media Session for playback.

The sequencer source ensures seamless playback by initializing and loading the next topology before the Media Session starts playing the current topology. This enables the application to start the next topology quickly whenever it is required.

The Media Session is responsible for feeding data to the sinks and playing the topologies in the sequence source. In addition, the Media Session manages the presentation time for the segments.

For more information about how the sequencer source manages topologies, see About the Sequencer Source.

This walk-through contains the following steps:

  1. Prerequisites
  2. Initializing Media Foundation
  3. Creating Media Foundation Objects
  4. Creating the Media Source
  5. Creating Partial Topologies
  6. Adding Topologies to the Sequencer Source
  7. Setting the First Topology on the Media Session
  8. Queuing the Next Topology on the Media Session
  9. Releasing the Sequencer Source

The code examples shown this topic are excerpts from the topic Sequencer Source Example Code, which contains the complete example code.

Prerequisites

Before starting this walk-through, familiarize yourself with the following Media Foundation concepts:

Also read How to Play Media Files with Media Foundation, because the example code shwon here expands on the code in that topic.

Initializing Media Foundation

Before you can use any Media Foundation interfaces or methods, initialize Media Foundation by calling the MFStartup function. For more information, see Initializing Media Foundation.

    hr = MFStartup(MF_VERSION);

Creating Media Foundation Objects

Next, create the following Media Foundation objects:

  • Media session. This object exposes the IMFMediaSession interface, which provides methods to play, pause, and stop the current topology.
  • Sequencer source. This object exposes the IMFSequencerSource interface, which provides methods to add, update, and delete topologies in a sequence.
  1. Call the MFCreateMediaSession function to create the Media Session.
  2. Call IMFMediaEventQueue::BeginGetEvent to request the first event from the Media Session.
  3. Call the MFCreateSequencerSource function to create the sequencer source.

The following code creates the Media Session and requests the first event:

//  Create a new instance of the media session.
HRESULT CPlayer::CreateSession()
{
    // Close the old session, if any.
    HRESULT hr = CloseSession();
    if (FAILED(hr))
    {
        goto done;
    }

    assert(m_state == Closed);

    // Create the media session.
    hr = MFCreateMediaSession(NULL, &m_pSession);
    if (FAILED(hr))
    {
        goto done;
    }

    // Start pulling events from the media session
    hr = m_pSession->BeginGetEvent((IMFAsyncCallback*)this, NULL);
    if (FAILED(hr))
    {
        goto done;
    }

    m_state = Ready;

done:
    return hr;
}

Creating the Media Source

Next, create a media source for the first playlist segment. Use the Source Resolver to create a media source from a URL. To do this, call the MFCreateSourceResolver function to create a source resolver and then call the IMFSourceResolver::CreateObjectFromURL method to create the media source.

For information about media sources, see Media Sources.

Creating Partial Topologies

Each segment in the sequencer source has its own partial topology. Next, create partial topologies for media sources. For a partial topology, the topology source nodes are connected directly to the output nodes, without specifying any intermediate transforms. The Media Session uses the topology loader object to resolve the topology. After a topology is resolved, the required decoders and other transform nodes are added. The sequencer source can also contain full topologies.

To create the topology object, use the MFCreateTopology function and then use the IMFTopologyNode interface to create stream nodes.

For complete instructions on using these programming elements to create topologies, see Creating Playback Topologies.

An application can play a selected portion of the native source by configuring the source node. To do this, set the MF_TOPONODE_MEDIASTART attribute and the MF_TOPONODE_MEDIASTOP attribute on MF_TOPOLOGY_SOURCESTREAM_NODE topology nodes. Specify the media start time and the media stop time relative to the start of the native source as UINT64 types.

Adding Topologies to the Sequencer Source

Next, add to the sequencer source the partial topologies that you created. Each sequence element, called a segment, is assigned an MFSequencerElementId identifier. For more information about how the sequencer source manages topologies, see About the Sequencer Source.

After all of the topologies are added to the sequencer source, the application must flag the last segment in the sequence to end playback in the pipeline. Without this flag, the sequencer source expects more topologies to be added.

  1. Call the IMFSequencerSource::AppendTopology method to add a specific topology to the sequencer source.

        hr = m_pSequencerSource->AppendTopology(
            pTopology, 
            SequencerTopologyFlags_Last, 
            &SegmentId
            );
    

    AppendTopology adds the specified topology to the sequence. This method returns the segment identifier in the pdwId parameter.

    If the topology is the last one in the sequencer source, pass SequencerTopologyFlags_Last in the dwFlags parameter. This value is defined in the MFSequencerTopologyFlags enumeration.

  2. Call IMFSequencerSource::UpdateTopologyFlags to update the flags for the topology associated with the segment identifier in the input list. In this case, the call indicates that the specified segment is the last segment in the sequencer. (This call is optional if the last topology is specified in the AppendTopology call.)

        BOOL bFirstSegment = (NumSegments() == 0);
    
        if (!bFirstSegment)
        {
            // Remove the "last segment" flag from the last segment.
            hr = m_pSequencerSource->UpdateTopologyFlags(LastSegment(), 0);
            if (FAILED(hr))
            {
                goto done;
            }
        }
    

The application can replace a segment's topology with another topology by calling the IMFSequencerSource::UpdateTopology and passing the new topology in pTopology. If there are new native sources in the new topology, the sources are added to the source cache. The preroll list is also refreshed.

Setting the First Topology on the Media Session

Next, queue the first topology in the sequence source on the Media Session. To get the first topology from the sequencer source, the application must call the IMFMediaSourceTopologyProvider::GetMediaSourceTopology method. This method returns the partial topology, which is resolved by the Media Session.

For information about partial topologies, see About Topologies.

  1. Retrieve the native media source for the first topology of the sequence source.

  2. Create a presentation descriptor for the media source by calling the IMFMediaSource::CreatePresentationDescriptor method.

  3. Retrieve the associated topology for the presentation by calling the IMFMediaSourceTopologyProvider::GetMediaSourceTopology method.

  4. Set the first topology on the Media Session by Calling IMFMediaSession::SetTopology.

    Call SetTopology with the dwSetTopologyFlags parameter set to NULL. This instructs the Media Session to start the specified topology when the current topology has been completed. Because in this case, the specified topology is the first topology and there is no current presentation, the Media Session starts the new presentation immediately.

    The NULL value also indicates that Media Session must resolve the topology because the topology returned by the topology provider is always a partial topology.

// Queues the next topology on the session.

HRESULT CPlaylist::QueueNextSegment(IMFPresentationDescriptor *pPD)
{
    IMFMediaSourceTopologyProvider *pTopoProvider = NULL;
    IMFTopology *pTopology = NULL;

    //Get the topology for the presentation descriptor
    HRESULT hr = m_pSequencerSource->QueryInterface(IID_PPV_ARGS(&pTopoProvider));
    if (FAILED(hr))
    {
        goto done;
    }

    hr = pTopoProvider->GetMediaSourceTopology(pPD, &pTopology);
    if (FAILED(hr))
    {
        goto done;
    }

    //Set the topology on the media session
    m_pSession->SetTopology(NULL, pTopology);

done:
    SafeRelease(&pTopoProvider);
    SafeRelease(&pTopology);
    return hr;
}

Queuing the Next Topology on the Media Session

Next, the application needs to handle the MENewPresentation event.

Sequencer source raises MENewPresentation when the Media Session starts playing a segment that has another segment following it. This event informs the application about the next topology in the sequence source by providing the presentation descriptor for the next segment in the preroll list. The application must retrieve the associated topology, by using the topology provider, and queue it on the Media Session. The sequencer source then prerolls this topology, which ensures a seamless transition between presentations.

When the application seeks across segments, the application receives several MENewPresentation events as the sequencer source refreshes the preroll list and sets up the correct topology. The application must handle each event and queue the topology returned in the event data, on the Media Session. For information about skipping segments, see Using the Sequencer Source.

For information about getting sequencer source notifications, see Sequencer Source Events.

  1. In the MENewPresentation event handler, retrieve the presentation descriptor for the next segment from the event data.

  2. Get the associated topology for the presentation by calling the IMFMediaSourceTopologyProvider::GetMediaSourceTopology method.

  3. Set the topology on the Media Session by calling the IMFMediaSession::SetTopology method.

    The Media Session starts the new presentation when the current presentation has been completed.

HRESULT CPlaylist::OnNewPresentation(IMFMediaEvent *pEvent)
{
    IMFPresentationDescriptor *pPD = NULL;

    HRESULT hr = GetEventObject(pEvent, &pPD);

    if (SUCCEEDED(hr))
    {
        // Queue the next segment on the media session
        hr = QueueNextSegment(pPD);
    }

    SafeRelease(&pPD);
    return hr;
}

Releasing the Sequencer Source

Finally, shut down the sequencer source. To do so, call the IMFMediaSource::Shutdown method on the sequencer source. This call shuts down all of the underlying native media sources in the sequencer source.

After releasing the sequencer source, the application should close and shut down the Media Session by calling IMFMediaSession::Close and IMFMediaSession::Shutdown, in that order.

To avoid memory leaks, the application must release pointers to Media Foundation interfaces when they are no longer needed.

Next Steps

This walk-through illustrated how to create a basic playlist by using the sequencer source. After creating the playlist, you might want to add advanced features such as segment skipping, changing the playback state, and seeking within a segment. The following list provides links to the related topics:

Sequencer Source