Share via


如何建立播放清單

本主題描述如何使用時序來源來播放一連串的檔案。

概觀

若要在序列中播放媒體檔案,應用程式必須在序列中新增拓撲,以建立播放清單,並將這些拓撲排入媒體會話以播放。

排序器來源可確保在媒體會話開始播放目前拓撲之前,初始化和載入下一個拓撲,以確保順暢播放。 這可讓應用程式在需要時快速啟動下一個拓撲。

媒體會話負責將資料摘要至接收,並在序列來源中播放拓撲。 此外,媒體會話會管理區段的簡報時間。

如需排序器來源如何管理拓撲的詳細資訊,請參閱 關於 Sequencer 來源

此逐步解說包含下列步驟:

  1. 先決條件
  2. 初始化媒體基礎
  3. 建立媒體基礎物件
  4. 建立媒體來源
  5. 建立部分拓撲
  6. 將拓撲新增至 Sequencer 來源
  7. 在媒體會話上設定第一個拓撲
  8. 佇列媒體會話上的下一個拓撲
  9. 釋放 Sequencer 來源

本主題所示的程式碼範例是 Sequencer 來源範例程式碼主題的摘錄,其中包含完整的範例程式碼。

必要條件

開始進行此逐步解說之前,請先熟悉下列媒體基礎概念:

另請參閱 如何使用 Media Foundation 播放媒體檔案,因為此處的範例程式碼 shwon 會展開該主題中的程式碼。

初始化媒體基礎

在您可以使用任何 Media Foundation 介面或方法之前,請先呼叫 MFStartup 函式來初始化 Media Foundation。 如需詳細資訊,請參閱 初始化媒體基礎

    hr = MFStartup(MF_VERSION);

建立媒體基礎物件

接下來,建立下列 Media Foundation 物件:

  • 媒體會話。 此物件會公開 IMFMediaSession 介面,其提供播放、暫停和停止目前拓撲的方法。
  • Sequencer 來源。 這個物件會公開 IMFSequencerSource 介面,這個介面提供在序列中新增、更新和刪除拓撲的方法。
  1. 呼叫 MFCreateMediaSession 函式 以建立媒體會話。
  2. 呼叫 IMFMediaEventQueue::BeginGetEvent 以向媒體會話要求第一個事件。
  3. 呼叫 MFCreateSequencerSource 函式來建立排序器來源。

下列程式碼會建立媒體會話,並要求第一個事件:

//  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;
}

建立媒體來源

接下來,建立第一個播放清單區段的媒體來源。 使用 來源解析程式 從 URL 建立媒體來源。 若要這樣做,請呼叫 MFCreateSourceResolver 函式來建立來源解析程式,然後呼叫 IMFSourceResolver::CreateObjectFromURL 方法來建立媒體來源。

如需媒體來源的相關資訊,請參閱 媒體來源

建立部分拓撲

排序器來源中的每個區段都有自己的部分拓撲。 接下來,建立媒體來源的部分拓撲。 針對部分拓撲,拓撲來源節點會直接連線到輸出節點,而不需要指定任何中繼轉換。 媒體會話會使用拓撲載入器物件來解析拓撲。 解析拓撲之後,會新增必要的解碼器和其他轉換節點。 排序器來源也可以包含完整的拓撲。

若要建立拓撲物件,請使用 MFCreateTopology 函式,然後使用 IMFTopologyNode 介面來建立資料流程節點。

如需使用這些程式設計項目來建立拓撲的完整指示,請參閱 建立播放拓撲

應用程式可以藉由設定來源節點來播放原生來源的選取部分。 若要這樣做,請在MF_TOPOLOGY_SOURCESTREAM_NODE拓撲節點上設定MF_TOPONODE_MEDIASTART屬性和MF_TOPONODE_MEDIASTOP屬性。 將媒體開始時間和媒體停止時間指定為 UINT64 類型,相對於原生來源的開始時間。

將拓撲新增至 Sequencer 來源

接下來,將 新增至排序器來源您所建立的部分拓撲。 每個稱為 區段的序列元素都會指派 MFSequencerElementId 識別碼。 如需排序器來源如何管理拓撲的詳細資訊,請參閱 關於 Sequencer 來源

將所有拓撲新增至排序器來源之後,應用程式必須標幟序列中的最後一個區段,才能結束管線中的播放。 如果沒有此旗標,排序器來源預期會新增更多拓撲。

  1. 呼叫 IMFSequencerSource::AppendTopology 方法,將特定拓撲新增至排序器來源。

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

    AppendTopology 會將指定的拓撲新增至序列。 這個方法會傳回 pdwId 參數中的區段識別碼。

    如果拓撲是排序器來源的最後一個拓撲,請在 dwFlags 參數中傳遞SequencerTopologyFlags_Last。 此值定義于 MFSequencerTopologyFlags 列舉中。

  2. 呼叫 IMFSequencerSource::UpdateTopologyFlags 來更新與輸入清單中區段識別碼相關聯的拓撲旗標。 在此情況下,呼叫會指出指定的區段是排序器中的最後一個區段。 (如果 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;
            }
        }
    

應用程式可以呼叫 IMFSequencerSource::UpdateTopology ,並在 pTopology中傳遞新的拓撲,以另一個拓撲取代區段的拓撲。 如果新的拓撲中有新的原生來源,則會將來源新增至來源快取。 預先註冊清單也會重新整理。

在媒體會話上設定第一個拓撲

接下來,將媒體會話上序列來源中的第一個拓撲排入佇列。 若要從排序器來源取得第一個拓撲,應用程式必須呼叫 IMFMediaSourceTopologyProvider::GetMediaSourceTopology 方法。 這個方法會傳回媒體會話所解析的部分拓撲。

如需部分拓撲的相關資訊,請參閱 關於拓撲

  1. 擷取序列來源第一個拓撲的原生媒體來源。

  2. 呼叫 IMFMediaSource::CreatePresentationDescriptor 方法,為媒體來源建立簡報描述元。

  3. 呼叫 IMFMediaSourceTopologyProvider::GetMediaSourceTopology 方法來擷取簡報的相關拓撲。

  4. 呼叫 IMFMediaSession::SetTopology,在媒體會話上設定第一個拓撲。

    呼叫 SetTopology,並將dwSetTopologyFlags 參數設定為 Null。 這會指示媒體會話在目前拓撲完成時啟動指定的拓撲。 在此情況下,指定的拓撲是第一個拓撲,而且沒有目前的簡報,媒體會話會立即啟動新的簡報。

    Null值也表示媒體會話必須解析拓撲,因為拓撲提供者傳回的拓撲一律是部分拓撲。

// 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;
}

佇列媒體會話上的下一個拓撲

接下來,應用程式必須處理 MENewPresentation 事件。

當媒體會話開始播放之後有另一個區段的區段時,Sequencer 來源會引發 MENewPresentation 。 此事件會提供預先註冊清單中下一個區段的簡報描述項,以通知應用程式順序來源中的下一個拓撲。 應用程式必須使用拓撲提供者來擷取相關聯的拓撲,並將它排入媒體會話。 排序器來源接著會預先註冊此拓撲,以確保簡報之間的順暢轉換。

當應用程式跨區段搜尋時,應用程式會收到數個 MENewPresentation 事件,因為排序器來源會重新整理預先註冊清單並設定正確的拓撲。 應用程式必須在媒體會話上處理每個事件,並將事件資料中傳回的拓撲排入佇列。 如需略過區段的資訊,請參閱 使用 Sequencer 來源

如需取得排序器來源通知的詳細資訊,請參閱 Sequencer 來源事件

  1. MENewPresentation 事件處理常式中,從事件資料擷取下一個區段的簡報描述元。

  2. 呼叫 IMFMediaSourceTopologyProvider::GetMediaSourceTopology 方法,以取得簡報的相關拓撲。

  3. 藉由呼叫 IMFMediaSession::SetTopology 方法,在媒體會話上設定拓撲。

    媒體會話會在目前的簡報完成時啟動新的簡報。

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;
}

釋放 Sequencer 來源

最後,關閉排序器來源。 若要這樣做,請在排序器來源上呼叫 IMFMediaSource::Shutdown 方法。 此呼叫會關閉排序器來源中的所有基礎原生媒體來源。

釋放排序器來源之後,應用程式應該依該順序呼叫 IMFMediaSession::CloseIMFMediaSession::Shutdown來關閉和關閉媒體會話。

若要避免記憶體流失,應用程式必須在不再需要時釋出媒體基礎介面的指標。

後續步驟

本逐步解說說明如何使用排序器來源建立基本播放清單。 建立播放清單之後,您可能會想要新增進階功能,例如區段略過、變更播放狀態,以及在區段內搜尋。 下列清單提供相關主題的連結:

Sequencer 來源