撰寫自定義媒體來源
本主題描述如何在 Microsoft Media Foundation 中實作自定義媒體來源。 其中包含下列各節:
建立簡報描述元
IMFMediaSource::CreatePresentationDescriptor 方法會傳回來源簡報描述元的複本。 若要建立簡報描述元,您必須知道來源內容中的數據流數目,以及每個數據流的可能格式。 針對每個數據流,建立數據流描述元,如下所示:
- 建立媒體類型的陣列。 陣列中的每個媒體類型都代表數據流的可能格式。 如需建立媒體類型的詳細資訊,請參閱 媒體類型。
- 呼叫 MFCreateStreamDescriptor 來建立數據流描述元。 傳入媒體類型的陣列。 函式會傳回 IMFStreamDescriptor 指標。
- 呼叫 IMFStreamDescriptor::GetMediaTypeHandler,以取得數據流描述元的媒體類型處理程式。
- 呼叫 IMFMediaTypeHandler::SetCurrentMediaType 來設定預設數據流格式。 使用您在步驟 1 中建立的其中一種媒體類型。 一般而言,您應該使用具有最高品質的格式。
- 選擇性地在數據流描述元上設定屬性。 如需套用至資料串流描述的屬性清單,請參閱 Stream 描述元屬性。
現在建立簡報描述元:
- 呼叫 MFCreatePresentationDescriptor,並傳入流描述符的陣列。 函式會傳回 IMFPresentationDescriptor 指標。
- 呼叫 IMFPresentationDescriptor::SelectStream 來選擇一或多個數據流,以確定預設的數據流。 在預設組態中至少必須選取一個數據流。
- 或者,在簡報描述元上設定屬性。 如需套用至數據流描述元的屬性清單,請參閱 Presentation Descriptor Attributes。
您應該在啟動時或在來源剖析足夠的源數據之後,建立簡報描述元一次,以判斷內容。 CreatePresentationDescriptor 方法應該會傳回簡報描述元的複本。 若要建立複本,請呼叫 IMFPresentationDescriptor::Clone。 傳回複本可防止客戶端修改原始簡報描述器的狀態,例如屬性或串流選擇。 不過,請注意,Clone 會建立淺層複本,因此用戶端可能會修改基礎數據流描述元。
啟動媒體來源
IMFMediaSource::Start 方法會啟動媒體來源或移動到新位置。 呼叫 Start 會導致在先前狀態暫停或執行時,搜尋,並指定新的開始時間。 否則,Start 方法會導致 start。 當 開始 作業完成時,傳送下列事件。
- 針對每個新串流傳送 MENewStream 事件,也就是先前已取消選取且現在已重新選取的每個串流。 事件數據是數據流的指標。
- 針對每個先前選取且仍被選取的數據流,傳送一個 MEUpdatedStream 事件。 事件數據是數據流的指標。 (請勿傳送已取消選取數據流的事件。)
- 如果來源正在搜尋,請傳送 MESourceSeeked 事件。 否則,傳送 MESourceStarted 事件。 事件數據是在 Start 方法中指定的開始時間。 針對MESourceStarted事件,如果開始時間是VT_EMPTY,請在事件上設定 MF_EVENT_SOURCE_ACTUAL_START 屬性。 屬性值是實際的開始時間。
- 針對每個數據流,如果來源正在搜尋,請傳送 MEStreamSeeked 事件。 否則,傳送 MEStreamStarted 事件。 事件數據是開始時間。 (媒體來源可以呼叫數據流的 IMFMediaEventGenerator::QueueEvent 方法,將數據流上的事件排入佇列。
當資料流取消選取時,請關閉資料流。 數據流不應該在該時間點再排入任何事件佇列。
pguidTimeFormat 參數中會指定 Start 方法的時間格式。 以 GUID_NULL表示的標準時間格式是 100 奈秒的單位。 媒體來源必須支持這個時間格式。
尋求
搜尋時,要求的開始位置可能不會落在確切的樣本界限上。 此外,針對壓縮的內容,開始位置可能會落在主要畫面格之間。 數據流應該會從最早的點傳遞樣本,以在要求的開始位置產生未壓縮的樣本。 針對視訊,這表示從上一個主要畫面格開始。 處理管線負責從解碼器刪除多餘的幀,以便在要求的時間開始播放。
來源事件中提供的開始時間(MESourceStarted、MESourceSeeked、MEStreamStarted和 MEStreamSeeked) 是要求開始時間 (Start 方法中提供的值),不論實際開始位置為何。
例如,假設視訊數據流的前幾個畫面具有下列特性:
樣本 | 1 | 2 | 3 | 4 |
---|---|---|---|---|
時間 | 33 毫秒 | 66 毫秒 | 100 毫秒 | 133 毫秒 |
主要畫面格? | 是的 | 不 | 不 | 是的 |
如果以 100 毫秒的值呼叫 Start 方法,則來源必須從此時間之前的第一個關鍵影格開始輸出視訊,即從影格 1 開始。 開始事件仍會指出事件資料中的 100 毫秒。
暫停媒體來源
IMFMediaSource::Pause 方法將暫停媒體來源。
當來源暫停時,數據流可以建立新的範例,並將其儲存在佇列上,但數據流不會傳遞範例。 以下是此規則的一些例外狀況:
- 實時來源應在暫停時丟棄數據。
- 如果來源從網路取得數據,它可能會暫停伺服器。
如果用戶端在來源暫停時呼叫 IMFMediaStream::RequestSample,則要求也會排入佇列,直到再次啟動來源為止。 不應忽略要求。
只有在啟動狀態下才允許暫停。 否則,應該暫停 並傳回 MF_E_INVALID_STATE_TRANSITION。
產生源數據
Media Foundation 會使用 提取模型,這表示數據流會產生並傳遞範例,以響應來自管線的要求。 當媒體來源正在運行且選擇了數據流時,數據流可以提供樣本。 只有當用戶端要求新的範例時,數據流才會傳遞數據。
範例要求
用戶端會呼叫 IMFMediaStream::RequestSample來要求新的範例。 以下是作業順序:
用戶端呼叫 IMFMediaStream::RequestSample。 參數是指向選擇性 令牌 物件的指標,客戶端使用該指標來追蹤請求。 用戶端會實作令牌。 令牌必須公開 IUnknown 介面。 用戶端也可以傳入NULL指標,而不是令牌。
如果用戶端提供了令牌,媒體串流會在令牌上呼叫 AddRef,並將令牌放入先進先出佇列中。 方法會傳回 ,其餘步驟會以異步方式進行。
當有更多數據可用時,媒體串流會建立新的範例。 (下一節將詳細說明此步驟。
媒體流會從佇列中取出第一個標記。
如果令牌未 NULL,媒體數據流會在媒體範例上設定 MFSampleExtension_Token 屬性。 屬性的值是標記的指標。
媒體串流會傳送 MEMediaSample 事件。 事件數據是範例 IMFSample 介面的指標。
如果用戶端提供令牌,媒體流會在令牌物件上呼叫 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 旗標。
- 第一個範例的時間戳應為零。
- 事件和串流狀態的處理方式與媒體來源相同,但暫停狀態除外。
- 暫停時,請勿將範例排入佇列。 卸除暫停時所產生的任何數據。
- 即時來源通常不支持搜尋、反向播放或速率控制。
相關主題