共用方式為


串流和應用程式線程

[與此頁面相關聯的功能,DirectShow是舊版功能。 它已被 MediaPlayerIMFMediaEngine,以及媒體基金會中的 音訊/視訊擷取取代。 這些功能已針對 Windows 10 和 Windows 11 進行優化。 Microsoft強烈建議新程式代碼盡可能在媒體 基礎中使用 MediaPlayerIMFMediaEngine 音訊/視訊擷取,而不是 DirectShow。 Microsoft建議使用舊版 API 的現有程式代碼,盡可能改寫成使用新的 API。]

任何 DirectShow 應用程式至少包含兩個重要的線程:應用程式線程,以及一或多個串流線程。 範例會在串流線程上傳遞,而狀態變更發生在應用程式線程上。 主要串流線程是由來源篩選器或剖析器篩選器所建立的。 其他篩選器可能會建立用於傳遞樣本的工作線程,而這些線程也會被視為串流線程。

某些方法會在應用程式線程上呼叫,而其他方法則是在串流線程上呼叫。 例如:

擁有個別的串流線程可讓數據在應用程式線程等候使用者輸入時流經圖形。 不過,多個線程的危險在於,篩選條件可能會在暫停時建立資源(在應用程式線程上)、在串流方法內使用這些資源,並在停止時加以終結(也會在應用程式線程上)。 如果您不小心,串流線程可能會在這些資源被銷毀之後嘗試使用它們。 解決方案是使用重要區段來保護資源,並同步處理串流方法與狀態變更。

篩選需要一個重要區段來保護篩選狀態。 CBaseFilter 類別具有這個重要區段的成員變數,CBaseFilter::m_pLock。 這個重要區段稱為篩選鎖定。 此外,每個輸入針腳都需要一個重要區段來保護串流線程所使用的資源。 這些關鍵區段稱為串流鎖,您必須在衍生的 pin 類別中宣告它們。 最簡單的方式是使用 CCritSec 類別,它會包裝 Windows CRITICAL_SECTION 物件,而且可以使用 CAutoLock類別鎖定。 CCritSec 類別也提供一些實用的偵錯函式。 如需詳細資訊,請參閱 重要區段偵錯函式

當濾鏡停止或清除時,必須使應用程式線程與串流線程同步運作。 若要避免死結,它必須先解除封鎖串流線程,這可能會因為數個原因而遭到封鎖:

  • 它正在等候在 IMemAllocator::GetBuffer 方法內取得範例,因為配置器的所有範例都正在使用中。
  • 它正在等候另一個過濾器從串流方法返回,例如 接收
  • 它會在自己的其中一個串流方法內等候,等某些資源變得可用。
  • 這是一個渲染器過濾器,正在等候下一個樣本的呈現時間。
  • 這是暫停時,在 Receive 方法內等候的轉譯器篩選。

因此,當篩選停止或排清時,它必須執行下列動作:

  • 以任何原因釋放它所持有的任何範例。 這麼做會解除封鎖 GetBuffer 方法
  • 儘快從任何串流方式返回。 如果串流方法正在等候資源,它必須立即停止等候。
  • 開始拒絕 Receive中的範例,讓串流線程無法再存取任何資源。 (CBaseInputPin 類別會自動處理此作業。
  • Stop 方法必須取消認可篩選的所有配置器。 (CBaseInputPin 類別會自動處理此作業。

排清和停止兩者都會發生在應用程式線程上。 濾鏡會依照 IMediaControl::Stop 方法而停止。 篩選器圖形管理器會以上游順序發出停止命令,從渲染器開始,反向操作到源篩選器。 stop 命令會在濾鏡的 CBaseFilter::Stop 方法內完全發生。 當方法傳回時,濾鏡應該處於已停止狀態。

排清通常會因為 seek 命令而發生。 排清命令會從來源或剖析器篩選開始,然後向下游移動。 排清會分兩個階段進行:IPin::BeginFlush 方法會通知篩選條件捨棄所有暫止和傳入的數據;IPin::EndFlush 方法會指示篩選條件再次接受數據。 排清過程需要兩個階段,因為 BeginFlush 呼叫是在應用程式執行緒上進行,而串流執行緒會繼續傳遞數據。 因此,某些範例可能會在 BeginFlush 呼叫之後到達。 篩選條件應該捨棄這些。 在 EndFlush 呼叫後抵達的任何樣本,都保證為新的,而且應該傳遞。

下列各節包含程式代碼範例,示範如何實作最重要的篩選方法,例如 暫停接收等等,以避免死結和競爭狀況。 不過,每個篩選都有不同的需求,因此您必須將這些範例調整為特定的篩選。

注意

CTransformFilterCTransInPlaceFilter 基類會處理本文所述的許多問題。 如果您正在撰寫轉換篩選,而且篩選條件不會等候串流方法內的事件,或保留 Receive以外的範例,則這些基類應該就足夠了。