ストリーミングスレッドとアプリケーションスレッド
[このページに関連付けられている機能 DirectShow は、従来の機能です。 MediaPlayer、IMFMediaEngine、Media Foundation のオーディオ/ビデオ キャプチャに置き換わりました。 これらの機能は、Windows 10とWindows 11用に最適化されています。 新しいコードでは、可能であれば、DirectShow ではなく Media Foundation で MediaPlayer、IMFMediaEngine、Audio/Video Capture を使用することを強くお勧めします。 Microsoft は、レガシ API を使用する既存のコードを、可能であれば新しい API を使用するように書き換えるよう提案しています。]
DirectShow アプリケーションには、少なくとも 2 つの重要なスレッド (アプリケーション スレッドと 1 つ以上のストリーミング スレッド) が含まれています。 サンプルはストリーミング スレッドで配信され、状態の変更はアプリケーション スレッドで行われます。 メイン ストリーミング スレッドは、ソース フィルターまたはパーサー フィルターによって作成されます。 他のフィルターでは、サンプルを配信するワーカー スレッドが作成される場合があり、これらはストリーミング スレッドと見なされます。
アプリケーション スレッドで呼び出されるメソッドもあれば、ストリーミング スレッドで呼び出されるメソッドもあります。 次に例を示します。
- ストリーミング スレッド: IMemInputPin::Receive、 IMemInputPin::ReceiveMultiple、 IPin::EndOfStream、 IMemAllocator::GetBuffer。
- アプリケーション スレッド: IMediaFilter::P ause、 IMediaFilter::Run、 IMediaFilter::Stop、 IMediaSeeking::SetPositions、 IPin::BeginFlush、 IPin::EndFlush。
- いずれか: IPin::NewSegment。
別のストリーミング スレッドを使用すると、アプリケーション スレッドがユーザー入力を待機している間に、データをグラフ内で流すことができます。 ただし、複数のスレッドの危険性は、フィルターが (アプリケーション スレッドで) 一時停止したときにリソースを作成し、ストリーミング メソッド内で使用し、停止したときに (アプリケーション スレッドでも) 破棄する可能性があるということです。 注意しないと、ストリーミング スレッドが破棄された後にリソースを使用しようとする可能性があります。 この解決策は、重要なセクションを使用してリソースを保護し、ストリーミング メソッドを状態の変更と同期することです。
フィルターには、フィルターの状態を保護するために重要なセクションが 1 つ必要です。 CBaseFilter クラスには、このクリティカル セクション CBaseFilter::m_pLock のメンバー変数があります。 この重要なセクションは、フィルター ロックと呼ばれます。 また、各入力ピンには、ストリーミング スレッドで使用されるリソースを保護するための重要なセクションが必要です。 これらの重要なセクションはストリーミング ロックと呼ばれます。それらを派生ピン クラスで宣言する必要があります。 Windows CRITICAL_SECTION オブジェクトをラップし、CAutoLock クラスを使用してロックできる CCritSec クラスを使用するのが最も簡単です。 CCritSec クラスには、便利なデバッグ関数も用意されています。 詳細については、「 Critical Section Debugging Functions」を参照してください。
フィルターが停止またはフラッシュされると、アプリケーション スレッドをストリーミング スレッドと同期する必要があります。 デッドロックを回避するには、まずストリーミング スレッドのブロックを解除する必要があります。これは、いくつかの理由でブロックされる可能性があります。
- アロケーターのすべてのサンプルが使用されているため、 IMemAllocator::GetBuffer メソッド内でサンプルを取得するのを待機しています。
- 別のフィルターがストリーミング メソッド ( Receive など) から返されるのを待機しています。
- 一部のリソースが使用可能になるまで、独自のストリーミング 方法の 1 つ内で待機しています。
- 次のサンプルのプレゼンテーション時間を待機しているレンダラー フィルターです
- これは、一時停止中に Receive メソッド内で待機しているレンダラー フィルターです。
したがって、フィルターが停止またはフラッシュされると、次の操作を行う必要があります。
- 何らかの理由で保持しているサンプルをリリースします。 これを行うと、 GetBuffer メソッドのブロックが解除されます。
- 可能な限り迅速に任意のストリーミング メソッドから戻ります。 ストリーミング メソッドがリソースを待機している場合は、すぐに待機を停止する必要があります。
- ストリーミング スレッドがそれ以上のリソースにアクセスしないように、 Receive でサンプルの拒否を開始します。 ( CBaseInputPin クラスはこれを自動的に処理します)。
- Stop メソッドは、フィルターのすべてのアロケーターをデコミットする必要があります。 ( CBaseInputPin クラスはこれを自動的に処理します)。
フラッシュと停止の両方がアプリケーション スレッドで行われます。 フィルターは、IMediaControl::Stop メソッドに応答して停止します。 フィルター グラフ マネージャーは、レンダラーから開始し、ソース フィルターに戻って作業を開始し、アップストリーム順に停止コマンドを発行します。 stop コマンドは、フィルターの CBaseFilter::Stop メソッド内で完全に実行されます。 メソッドが戻るとき、フィルターは停止状態である必要があります。
フラッシュは通常、seek コマンドが原因で発生します。 フラッシュ コマンドは、ソースフィルターまたはパーサー フィルターから開始され、ダウンストリームに移動します。 フラッシュは 2 つの段階で行われます。 IPin::BeginFlush メソッドは、保留中のデータと受信データをすべて破棄するようにフィルターに通知します。 IPin::EndFlush メソッドは、データを再び受け入れるようにフィルターに通知します。 BeginFlush 呼び出しがアプリケーション スレッド上にあり、ストリーミング スレッドが引き続きデータを配信するため、フラッシュには 2 つのステージが必要です。 したがって、一部のサンプルは BeginFlush 呼び出しの後に到着する可能性があります。 フィルターはこれらを破棄する必要があります。 EndFlush 呼び出しの後に到着するすべてのサンプルは新しいものであり、配信する必要があります。
以下のセクションには、デッドロックや競合状態を回避する方法で、 Pause、 Receive などの最も重要なフィルター メソッドを実装する方法を示すコード サンプルが含まれています。 ただし、すべてのフィルターには異なる要件があるため、これらの例を特定のフィルターに適応させる必要があります。
注意
CTransformFilter および CTransInPlaceFilter 基本クラスは、この記事で説明されている多くの問題を処理します。 変換フィルターを記述していて、フィルターがストリーミング メソッド内のイベントを待機しない場合、または Receive の外部のサンプルを保持しない場合は、これらの基底クラスで十分です。