撰寫自訂媒體來源

本主題描述如何在 Microsoft Media Foundation 中實作自訂媒體來源。 它包含下列區段:

建立簡報描述元

IMFMediaSource::CreatePresentationDescriptor方法會傳回來源簡報描述元的複本。 若要建立簡報描述元,您必須知道來源內容中的資料流程數目,以及每個資料流程的可能格式。 針對每個資料流程,建立資料流程描述元,如下所示:

  1. 建立媒體類型的陣列。 陣列中的每個媒體類型都代表資料流程的可能格式。 如需建立媒體類型的詳細資訊,請參閱 媒體類型
  2. 呼叫 MFCreateStreamDescriptor 以建立資料流程描述元。 傳入媒體類型的陣列。 函式會傳回 IMFStreamDescriptor 指標。
  3. 呼叫 IMFStreamDescriptor::GetMediaTypeHandler 以取得資料流程描述元的媒體類型處理常式。
  4. 呼叫 IMFMediaTypeHandler::SetCurrentMediaType 來設定預設資料流程格式。 使用您在步驟 1 中建立的其中一種媒體類型。 一般而言,您應該使用具有最高品質的格式。
  5. 選擇性地在資料流程描述元上設定屬性。 如需套用至資料流程描述元的屬性清單,請參閱 串流描述元屬性

現在建立簡報描述元:

  1. 呼叫 MFCreatePresentationDescriptor 並傳入資料流程描述元的陣列。 函式會傳回 IMFPresentationDescriptor 指標。
  2. 呼叫 IMFPresentationDescriptor::SelectStream 來選取一或多個資料流程,以選擇預設資料流程選取範圍。 預設組態中至少必須選取一個資料流程。
  3. 選擇性地在簡報描述項上設定屬性。 如需套用至資料流程描述元的屬性清單,請參閱 簡報描述元屬性

您應該在啟動時或來源剖析足夠的來源資料之後,建立簡報描述元一次,以判斷內容。 CreatePresentationDescriptor方法應該會傳回簡報描述元的複本。 若要建立複本,請呼叫 IMFPresentationDescriptor::Clone。 傳回復本可防止用戶端修改原始簡報描述項的狀態,例如屬性或資料流程選取範圍。 不過,請注意 ,複製 會建立淺層複本,因此用戶端可能會修改基礎資料流程描述元。

啟動媒體來源

IMFMediaSource::Start方法會啟動媒體來源或搜尋新位置。 呼叫 Start 會導致在先前狀態暫停或執行時進行 搜尋 ,並指定新的開始時間。 否則, Start 方法會導致 啟動啟動作業完成時,傳送下列事件。

  1. 傳送每個新資料流程的 MENewStream 事件,也就是先前已取消選取且現在選取的每個資料流程。 事件資料是資料流程的指標。
  2. 針對先前選取且仍然選取的每個資料流程傳送 MEUpdatedStream 事件。 事件資料是資料流程的指標。 (請勿傳送已取消選取資料流程的事件。)
  3. 如果來源正在搜尋,請傳送 MESourceSeeked 事件。 否則,傳送 MESourceStarted 事件。 事件資料是在 Start 方法中指定的開始時間。 針對 MESourceStarted 事件,如果開始時間是VT_EMPTY,請在事件上設定 MF_EVENT_SOURCE_ACTUAL_START 屬性。 屬性值是實際的開始時間。
  4. 針對每個資料流程,如果來源正在搜尋,請傳送 MEStreamSeeked 事件。 否則,傳送 MEStreamStarted 事件。 事件資料是開始時間。 (媒體來源可以藉由呼叫資料流程的 IMFMediaEventGenerator::QueueEvent 方法..) ,將資料流程上的事件排入佇列

取消選取資料流程時,關閉資料流程。 資料流程不應該在該時間點將更多事件排入佇列。

Start方法的時間格式是在pguidTimeFormat參數中提供。 以 GUID_Null表示的標準時間格式是 100 奈秒單位。 媒體來源必須支援這個時間格式。

尋求

搜尋時,要求的開始位置可能不會落在確切的樣本界限上。 此外,針對壓縮的內容,開始位置可能會落在主要畫面格之間。 資料流程應該從最早的點傳遞樣本,以在要求的開始位置產生未壓縮的樣本。 對於影片,這表示從上一個主要畫面格開始。 管線負責從解碼器卸載額外的畫面格,讓播放從要求的時間開始。

來源事件中所指定的開始時間 (MESourceStartedMESourceSeekedMEStreamStartedMEStreamSeeked) 是要求開始時間, (Start 方法中指定的 值) ,不論實際的開始位置為何。

例如,假設影片資料流程的前幾個畫面具有下列特性:

範例 1 2 3 4
Time 33 毫秒 66 毫秒 100 毫秒 133 毫秒
主要畫面格?

 

如果使用 100 毫秒的值呼叫 Start 方法,來源必須輸出從畫面 1 開始的視訊,這是目前之前的第一個主要畫面格。 開始事件仍會指出事件資料中的 100 毫秒。

暫停媒體來源

IMFMediaSource::P ause方法會暫停媒體來源。

當來源暫停時,串流可以建立新的範例,並將其儲存在佇列上,但資料流程不會傳遞範例。 以下是此規則的一些例外狀況:

  • 即時來源應該會在暫停時卸載資料。
  • 如果來源從網路取得資料,它可能會暫停伺服器。

如果用戶端在來源暫停時呼叫 IMFMediaStream::RequestSample ,則要求也會排入佇列,直到再次啟動來源為止。 不應該卸載要求。

暫停只允許從啟動狀態。 否則, Pause 應該會 傳回MF_E_INVALID_STATE_TRANSITION

產生來源資料

媒體基礎會使用 提取模型,這表示串流會產生並傳遞範例,以回應來自管線的要求。 當媒體來源正在執行並選取資料流程時,串流可以傳遞範例。 只有在用戶端要求新的範例時,資料流程才會傳遞資料。

範例要求

用戶端會呼叫 IMFMediaStream::RequestSample來要求新的範例。 以下是作業順序:

  1. 用戶端會呼叫 IMFMediaStream::RequestSample。 引數是用戶端用來追蹤要求的選擇性 Token 物件的指標。 用戶端會實作權杖。 權杖必須公開 IUnknown 介面。 用戶端也可以傳入 Null 指標,而不是權杖。

  2. 如果用戶端提供權杖,媒體資料流程會在權杖上呼叫 AddRef ,並將權杖放在先進先出佇列中。 方法會傳回,其餘步驟會以非同步方式發生。

  3. 當有更多資料可用時,媒體串流會建立新的範例。 (下一節會更詳細地說明此步驟。)

  4. 媒體資料流程會從佇列提取第一個權杖。

  5. 如果權杖不是 Null,媒體資料流程會在媒體範例上設定 MFSampleExtension_Token 屬性。 屬性的值是標記的指標。

  6. 媒體資料流程會傳送 MEMediaSample 事件。 事件資料是範例 之 IMFSample 介面的指標。

  7. 如果用戶端提供權杖,媒體資料流程會在權杖物件上呼叫 Release

如果媒體資料流程無法滿足用戶端 的 RequestSample 要求,它會從佇列提取權杖,並在權杖上呼叫 Release ,但不會傳送 MEMediaSample 事件。

用戶端可以使用權杖來追蹤要求的狀態。 當用戶端收到 MEMediaSample 事件時,它可以從範例取得權杖,並將其與原始要求相符。 用戶端也可以使用權杖來偵測媒體來源是否已卸載要求。 如果權杖的參考計數落為零,且媒體資料流程不會傳送 MEMediaSample 事件,表示要求已卸載。

此處所列的步驟假設 RequestSample 方法會實作為非同步作業。 如果方法是同步的,您就不需要將要求權杖放在佇列上。 不過,如果產生資料需要很長的時間,建議使用非同步方法,例如,如果來源從位元組資料流程讀取資料。

資料流程負責緩衝處理 任何在 RequestSample呼叫之間累積的資料。

當媒體串流到達資料流程結尾時,它會在最後一個範例之後傳送 MEEndOfStream 事件。 每個資料流程結束之後,媒體來源會傳送 MEEndOfPresentation 事件。 在媒體串流傳送 MEEndOfStream 事件之後, RequestSample 方法會傳回 MF_E_END_OF_STREAM ,直到來源重新開機為止。

配置範例

當資料流程準備好填滿擱置的範例要求時,它會建立新的範例,並將一或多個媒體緩衝區新增至範例。 如需建立媒體緩衝區的詳細資訊,請參閱 媒體緩衝區

如果已知,資料流程必須設定時間戳記和持續時間。 時間戳記相對於來源。 在大部分情況下,內容的開頭會對應至時間戳記為零。 例如,如果來源從媒體檔案讀取,則檔案的開頭會有零的時間戳記。

範例上的時間戳記不一定等於簡報時間。 媒體會話會從來源時間轉譯為簡報時間。 對於壓縮的資料,資料流程應該會在開始時間之前從最接近的主要畫面格開始產生資料。 這可讓解碼器傳遞出現在所要求開始時間的框架。 (否則,解碼器必須等到下列主要畫面格。)

如果播放速率比 1.0 快或慢,管線會調整簡報時鐘的速率。 來源不會調整範例上的時間戳記。

來源可以藉由設定屬性來設定範例的其他資訊。 如需範例屬性的清單,請參閱 範例屬性

資料流程中的間距

如果資料流程包含長短的間距,建議資料流程傳送 MEStreamTick 事件。 此事件會通知用戶端遺漏範例。 事件資料是遺漏範例的時間戳記,以 100 奈秒為單位, (VT_I8) 。 此事件可以儲存下游元件,以等候不會送達的範例。 資料流程可以視需要傳送多少 MEStreamTick 事件,以跨越資料流程中的間距。

關閉媒體來源

當用戶端使用媒體來源完成時,它會呼叫 IMFMediaSource::Shutdown。 在此方法中,媒體來源應該會中斷任何迴圈參考計數。 一般而言,媒體來源與媒體資料流程之間會有迴圈參考。

如果您使用事件佇列來實作 IMFMediaEventGenerator,請在事件佇列上呼叫 IMFMediaEventQueue::Shutdown 。 這個方法會關閉事件佇列,併發出目前正在等候事件的任何呼叫端發出訊號。

關閉之後,來源上的所有方法都會傳回 MF_E_SHUTDOWN,但 IUnknown 方法除外。

即時來源

從 Windows 7 開始,Media Foundation 會自動支援音訊和視訊擷取裝置。 針對視訊,裝置必須在影片擷取類別中提供核心串流 (KS) 迷你驅動程式。 媒體基礎會使用 PnP 路徑來列舉裝置。 針對音訊,Media Foundation 會使用 Windows 多媒體裝置 (MMDevice) API 來列舉音訊端點裝置。 如果裝置符合這些準則,就不需要實作自訂媒體來源。

不過,您可能想要為某些其他類型的裝置或其他即時資料源實作自訂媒體來源。 即時來源與其他媒體來源之間只有一些差異:

  • IMFMediaSource::GetCharacteristics 方法中,傳回 MFMEDIASOURCE_IS_LIVE 旗標。
  • 第一個範例的時間戳記應為零。
  • 事件和串流狀態的處理方式與媒體來源相同,但暫停狀態除外。
  • 暫停時,請勿將範例排入佇列。 卸載暫停時產生的任何資料。
  • 即時來源通常不支援搜尋、反向播放或速率控制。

媒體來源

教學課程:撰寫自訂媒體來源