串流和應用程式執行緒

[與此頁面 相關的功能 DirectShow是舊版功能。 它已被 MediaPlayerIMFMediaEngineMedia Foundation 中的音訊/視訊擷取取代。 這些功能已針對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
  • 它會在自己的其中一個串流方法內等候,讓某些資源可供使用。
  • 這是正在等候下一個範例呈現時間的轉譯器篩選
  • 這是暫停時在 Receive 方法內等候的轉譯器篩選。

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

  • 釋放它基於任何原因而保留的任何範例。 這麼做會解除封鎖 GetBuffer 方法。
  • 儘快從任何串流方法傳回。 如果串流方法正在等候資源,它必須立即停止等候。
  • 開始拒絕 接收中的範例,讓串流執行緒無法存取任何其他資源。 (CBaseInputPin 類別會自動處理此作業。)
  • Stop方法必須取消認可篩選的所有配置器。 (CBaseInputPin 類別會自動處理此作業。)

排清和停止兩者都會在應用程式執行緒上發生。 篩選會停止回應 IMediaControl::Stop 方法。 Filter Graph 管理員會以上游順序發出 stop 命令,從轉譯器開始,並回溯處理到來源篩選。 stop 命令會在篩選的 CBaseFilter::Stop 方法內完全發生。 當方法傳回時,篩選準則應該處於停止狀態。

排清通常會因為 seek 命令而發生。 排清命令會從來源或剖析器篩選開始,然後流向下游。 排清會在兩個階段發生: IPin::BeginFlush 方法會通知篩選以捨棄所有擱置和傳入的資料; IPin::EndFlush 方法會指示篩選準則再次接受資料。 排清需要兩個階段,因為 BeginFlush 呼叫是在應用程式執行緒上,而串流執行緒會在其中繼續傳遞資料。 因此,某些範例可能會在 BeginFlush 呼叫之後送達。 篩選應該捨棄這些篩選準則。 在 EndFlush呼叫之後抵達的任何範例保證都是新的,而且應該傳遞。

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

注意

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