次の方法で共有


ストリーミングとアプリケーション スレッド

DirectShow アプリケーションには、少なくとも 2 つの重要なスレッドが含まれる。アプリケーション スレッドと 1 つ以上のストリーミング スレッドである。サンプルはストリーミング スレッドで送信され、状態の変更はアプリケーション スレッドで起こる。メイン ストリーミング スレッドはソース フィルタまたはパーサー フィルタにより作成される。他のフィルタはサンプルを送信するワーカー スレッドを作成することがあり、これらのスレッドもストリーミング スレッドと見なされる。

アプリケーション スレッドで呼び出されるメソッドと、ストリーミング スレッドで呼び出されるメソッドがある。次に例を示す。

異なるストリーミング スレッドがあると、アプリケーション スレッドがユーザー入力を待つ間にグラフにデータを流せる。しかし、複数のスレッドがある危険性として、フィルタは (アプリケーション スレッドで) ポーズ中にリソースを作成し、ストリーミング メソッド内でそのリソースを使い、(やはりアプリケーション スレッドで) 停止したときにそのリソースを破棄することがある。注意しないと、ストリーミング スレッドは破棄された後のリソースを使用しようとする可能性がある。対策としては、クリティカル セクションを使うリソースを保護し、ストリーミング メソッドを状態の変更と同期することである。

フィルタは、フィルタ状態を保護するクリティカル セクションを 1 つ必要とする。CBaseFilter クラスには、このクリティカル セクション用のメンバ変数 CBaseFilter::m_pLock がある。このクリティカル セクションは "フィルタ ロック" と呼ばれる。また、各入力ピンは、ストリーミング スレッドが使うリソースを保護するクリティカル セクションを必要とする。このクリティカル セクションは "ストリーミング ロック" と呼ばれる。派生ピン クラスで宣言する必要がある。最も簡単な方法は、CCritSec クラスを使うことである。このクラスは Windows CRITICAL_SECTION オブジェクトをラップする。CCritSec クラスは CAutoLock クラスを使ってロックできる。また、CCritSec クラスは有用なデバッグ関数も備えている。詳細については、「クリティカル セクション デバッグ関数」を参照すること。

フィルタは、停止するかフラッシュするとき、アプリケーション スレッドをストリーミング スレッドと同期する必要がある。デッドロックを避けるため、初めにストリーミング スレッドの動作の停止を解除する必要がある。ストリーミング スレッドは次のいくつかの理由で動作を停止することがある。

  • アロケータのすべてのサンプルが使用中であるため、IMemAllocator::GetBuffer メソッド内でサンプルを取得するまで待機している。
  • 別のフィルタが Receive などのストリーミング メソッドから返るまで待機している。
  • リソースが利用できるようになるまで、それ自体のストリーミング メソッド内で待機している。
  • 次のサンプルのプレゼンテーション時間まで待機するレンダリング フィルタである。
  • ポーズ中、Receive 内で待機するレンダリング フィルタである。

したがって、フィルタが停止するかフラッシュするときに、次の処理を行う必要がある。

  • どのような理由であっても、保持しているサンプルを解放する。そうすると、GetBuffer メソッドの動作の停止は解除される。
  • できるだけ早くストリーミング メソッドから返る。ストリーミング メソッドがリソースを待っている場合、すぐに待機を中止する必要がある。
  • ストリーミング スレッドがそれ以上リソースにアクセスしないように、Receive でサンプルの拒否を開始する。(CBaseInputPin クラスはこれを自動的に処理する。)
  • Stop メソッドはフィルタのすべてのアロケータをデコミットする必要がある。(CBaseInputPin クラスはこれを自動的に処理する。)

フラッシュと停止はどちらもアプリケーション スレッドで行われる。フィルタは IMediaControl::Stop メソッドに応答して停止する。フィルタ グラフ マネージャは、レンダラから始めてソース フィルタまでアップストリーム方向へ順に停止コマンドを発行する。停止コマンドはフィルタの CBaseFilter::Stop メソッド内で完全に実行される。メソッドが返ると、フィルタは停止状態になる。

通常、フラッシュはシーク コマンドにより実行される。フラッシュ コマンドはソース フィルタかパーサー フィルタで始まり、ダウンストリーム方向に移動する。フラッシュの処理は 2 段階で行われる。IPin::BeginFlush メソッドはペンディング状態のデータと受信データをすべて破棄するようにフィルタに通知する。IPin::EndFlush メソッドはもう一度データを受け入れるようにフィルタに通知する。フラッシュに 2 段階の処理が必要な理由は、BeginFlush 呼び出しがアプリケーション スレッドで行われている間、ストリーミング スレッドはデータの送信を続けているためである。したがって、BeginFlush 呼び出しの後に到着するサンプルもある。フィルタはこのようなサンプルを破棄する必要がある。EndFlush 呼び出しの後に到着するサンプルは新しいことが保証されており、送信する必要がある。

この後に続くサンプル コードでは、デッドロックとの競合状態を避けて PauseReceive など、最も重要なフィルタ メソッドを実装する方法を示す。しかし、それぞれのフィルタは要件が異なるため、自分の特定のフィルタに合わせてこれらの例を変更する必要がある。

 :  CTransformFilter および CTransInPlaceFilter の各基底クラスは、ここで説明している多くの問題を処理する。変換フィルタを作成し、"さらに" フィルタがストリーミング メソッド内のイベントを待たないか、Receive の外側にサンプルを保持しない場合は、これらの基底クラスで十分である。