如何撰寫 DirectShow 的來源篩選

[與此頁面相關的功能 DirectShow是舊版功能。 它已被 MediaPlayerIMFMediaEngineMedia Foundation 中的音訊/視訊擷取取代。 這些功能已針對Windows 10和Windows 11進行優化。 Microsoft 強烈建議新程式碼盡可能使用 MediaPlayerIMFMediaEngine音訊/視訊擷取 ,而不是 DirectShow。 Microsoft 建議使用舊版 API 的現有程式碼盡可能重寫為使用新的 API。

本主題描述如何撰寫 DirectShow 的自訂來源篩選。

注意

本主題僅描述推送來源;它不會描述提取來源,例如非同步讀取器篩選或連線至提取來源的分割器篩選。 如需 推送提取 來源之間的差異,請參閱 篩選開發人員的資料流程

DirectShow 串流模型

當您撰寫來源篩選時,請務必瞭解推送來源與即時來源不同。 即時來源會從某些外部來源取得資料,例如相機或網路串流。 一般而言,即時來源無法控制資料的傳入速率。 如果下游篩選準則沒有足夠的速度取用資料,來源將需要卸載樣本。

但是,推送來源不一定是即時來源。 例如,推送來源可以從本機檔案讀取資料。 在此情況下,下游轉譯器篩選會根據參考時鐘和範例時間戳記,決定其取用來來源資料的速度。 來源篩選準則會儘快傳遞樣本,但實際的資料流程會受到轉譯器的限制。 篩選開發人員的資料流程會說明控制資料流程的機制。

來源篩選上的每個輸出針腳都會建立稱為 串流執行緒的執行緒。 針腳會在串流執行緒上提供範例。 一般而言,所有解碼、處理和轉譯都會在此執行緒上發生,不過有些下游篩選可能會建立額外的執行緒來排入輸出樣本的佇列。

串流執行緒會執行具有下列結構的迴圈:

until (stopped)
  1. Get a media sample from the allocator.
  2. Fill the sample with data.
  3. Time stamp the sample. 
  4. Deliver the sample downstream.

如果沒有可用的範例,步驟 1 會封鎖直到範例可供使用為止。 步驟 4 也可以封鎖;例如,它可以在圖表暫停時封鎖。

迴圈會儘快執行,但受限於轉譯器篩選轉譯每個樣本的速度。 假設篩選圖形有參考時鐘,則速率取決於樣本上的呈現時間。 如果沒有參考時鐘,轉譯器會儘快取用樣本。

使用 CSource 和 CSourceStream

DirectShow 基類包含兩個支援推送來源的類別:CSource 和 CSourceStream

輸出針腳

來源篩選可以有多個輸出針腳。 在篩選的建構函式方法中,建立一或多個衍生自 CSourceStream 的針腳, (每個輸出資料流程) 一個針腳。 您不需要儲存針腳的指標;針腳會在建立時自動新增至篩選。

輸出格式

輸出針腳會使用下列 CSourceStream 方法處理格式交涉:

方法 Description
GetMediaType 從輸出釘選取得媒體類型。
針腳必須建議至少一個媒體類型,因為下游篩選可能不會建議任何類型。 在大部分情況下,下游篩選準則會是解碼器或轉譯器,視來源篩選是否傳遞壓縮或未壓縮的資料而定。 轉譯器篩選通常需要完整的媒體類型,其中包含轉譯資料流程所需的所有格式資訊。 對於解碼器,媒體類型中所需的資訊量取決於編碼格式。
CheckMediaType 檢查輸出針腳是否接受指定的媒體類型。 根據您實作 GetMediaType的方式而定,覆寫此方法是選擇性的。

GetMediaType方法已多載:

如果來源篩選的輸出針腳只支援一種媒體格式,您應該覆寫 (1) ,以使用該格式初始化 CMediaType 物件。 保留預設實作 (2) ,同時保留 CheckMediaType的預設實作。

如果針腳支援多個格式,請覆寫 (2) 。 根據索引變數的值,初始化 CMediaType 物件。 針腳應該會將格式傳回為已排序的清單。 在此情況下,您也必須覆寫 CheckMediaType ,以根據格式清單檢查媒體類型。

針對未壓縮的視訊格式,請記住下游篩選準則可以建議具有各種步進值的格式。 您的篩選應該接受任何有效的進位值。 如需詳細資訊,請參閱 BITMAPINFOHEADER

您也必須覆寫 pure-virtual CBaseOutputPin::D ecideBufferSize 方法。 使用這個方法來設定範例緩衝區的大小。

串流

CSourceStream類別會建立釘選的串流執行緒。 執行緒程式會在 CSourceStream::D oBufferProcessingLoop 方法中實作。 這個方法會呼叫衍生類別必須覆寫的 pure-virtual CSourceStream::FillBuffer 方法。 此方法是針腳填滿緩衝區的資料的位置。 例如,如果您的篩選提供未壓縮的視訊,這就是您要繪製視訊畫面的位置。

當篩選暫停或停止時,基類會自動在正確的時間啟動和停止執行緒迴圈。 發生這種情況時, CSourceStream 類別會呼叫一些方法來通知衍生類別:

如果您需要新增任何特殊處理,您可以覆寫這些方法。 否則,預設實作只會傳回 S_OK

尋求

如果您有具有一個輸出針腳的來源篩選,您可以使用 CSourceSeeking 類別作為實作搜尋的起點。 從 CSourceStreamCSourceSeeking繼承您的 pin 類別。

注意

不建議針對具有多個輸出針腳的篩選使用CSourceSeeking。 主要問題是只有一個針腳應該回應搜尋要求。 這通常需要針腳和篩選之間的通訊。

CSourceSeeking類別會管理播放速率、開始時間、停止時間和持續時間。 您的衍生類別應該設定初始停止時間和持續時間。 每當其中一個值變更時,就會視需要呼叫 CSourceSeeking::ChangeRateCSourceSeeking::ChangeStartCSourceSeeking::ChangeStop 方法。 方法全都是純虛擬方法。 衍生的 pin 類別會覆寫這些方法來執行下列動作:

  1. 在下游針腳上呼叫 IPin::BeginFlush 。 這會導致下游篩選準則釋放其持有的樣本,並拒絕新的樣本。
  2. 呼叫 CSourceStream::Stop 以停止串流執行緒。 來源篩選會暫停產生新資料。
  3. 在下游針腳上呼叫 IPin::EndFlush 。 這表示下游篩選準則接受新的資料。
  4. 使用新的開始和停止時間和速率呼叫 IPin::NewSegment
  5. 在下一個範例上設定不連續屬性。

如需詳細資訊,請參閱 支援在來源篩選中搜尋

如果您的篩選支援搜尋,資料流程位置現在與簡報時間無關。 搜尋之後,時間戳記會重設為零。 時間戳記的一般公式為:

  • 範例開始時間 = 經過的時間/播放速率
  • sample end time = sample start time + (time per frame / playback rate)

其中 經過時間 是篩選開始執行或上次搜尋命令之後經過的時間。

搜尋的時間格式

根據預設,搜尋命令單位為 100 奈秒。 您的來源篩選可以支援額外的時間格式,例如依畫面格編號搜尋。 每次 GUID 識別格式時;請參閱 時間格式 GUID

若要支援額外的時間格式,您必須在輸出釘選上實作下列方法:

如果應用程式設定新的時間格式, IMediaSeeking 方法中的所有位置參數都會以新的時間格式來解譯。 例如,如果時間格式是框架, IMediaSeeking::GetDuration 方法必須傳回畫面中的持續時間。

實際上,少數 DirectShow 篩選支援額外的時間格式,因此,少數 DirectShow 應用程式會使用這項功能。

撰寫來源篩選