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

[このページに関連付けられている機能 DirectShow は、従来の機能です。 MediaPlayer、IMFMediaEngine、Media Foundation のオーディオ/ビデオ キャプチャに置き換わりました。 これらの機能は、Windows 10とWindows 11用に最適化されています。 新しいコードでは、可能であれば、DirectShow ではなく Media Foundation で MediaPlayerIMFMediaEngineAudio/Video Capture を使用することを強くお勧めします。 Microsoft は、レガシ API を使用する既存のコードを、可能であれば新しい API を使用するように書き換えるよう提案しています。]

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

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

別のストリーミング スレッドを使用すると、アプリケーション スレッドがユーザー入力を待機している間に、データをグラフ内で流すことができます。 ただし、複数のスレッドの危険性は、フィルターが (アプリケーション スレッドで) 一時停止したときにリソースを作成し、ストリーミング メソッド内で使用し、停止したときに (アプリケーション スレッドでも) 破棄する可能性があるということです。 注意しないと、ストリーミング スレッドが破棄された後にリソースを使用しようとする可能性があります。 この解決策は、重要なセクションを使用してリソースを保護し、ストリーミング メソッドを状態の変更と同期することです。

フィルターには、フィルターの状態を保護するために重要なセクションが 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 呼び出しの後に到着するすべてのサンプルは新しいものであり、配信する必要があります。

以下のセクションには、デッドロックや競合状態を回避する方法で、 PauseReceive などの最も重要なフィルター メソッドを実装する方法を示すコード サンプルが含まれています。 ただし、すべてのフィルターには異なる要件があるため、これらの例を特定のフィルターに適応させる必要があります。

注意

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