Microsoft DirectX 8.0

C++ による DVD アプリケーションの開発

ここでは、C++ で DVD アプリケーションを作成するための主要な手順について説明する。ここで示したコード例のほとんどは、DirectShow 8.0 DVD サンプル アプリケーションから取ったものである。このアプリケーションは、DirectShow SDK のサンプル ディレクトリに格納されている。ここには、以下のトピックが含まれる。DVD の基本的な一般概念の背景情報については、「DVD の基礎」を参照すること。

DVD アプリケーション開発の基礎

DVD アプリケーションを開発する際には、ユーザー インターフェイス用のコードを用意し、Microsoft® DirectShow® アプリケーション プログラミング インターフェイス (API) を呼び出して DVD の再生と操作のためのすべてのコマンドを実行する。これには、Component Object Model (COM) における DVDGraphBuilder オブジェクトの CoCreateInstance メソッドの呼び出しが含まれる。これにより、DVD フィルタ グラフを作成する実際の作業が行われる。フィルタ グラフが作成されたら、DVD ナビゲータ ソース フィルタによって公開される IDvdControl2 および IDvdInfo2 インターフェイスへのポインタを取得できる。インターフェイス ポインタを取得したら、インターフェイスを PlayTitlePause などの DVD 操作コマンドに結びつける。この基本プロセスは非常に単純で、他の種類の DirectShow アプリケーションの作成と似ている。

DVD フィルタ グラフの構成

一般に、DVD フィルタ グラフは自動的に作成されるため、アプリケーション開発者はその詳細について関わる必要はない。このトピックは、内部で何が行われているかを知りたい人のために用意されている (ここで触れている個々のフィルタの詳細については、「フィルタ」を参照すること)。

次の図に、標準的な DVD フィルタ グラフを示す。

DVD フィルタ グラフ

DVD ナビゲータは、DVD 再生フィルタ グラフの最初のフィルタであり、以下の作業を行う。

オーディオ ストリームに対しては、DVD ナビゲータの後にオーディオ デコーダが接続され、それはさらにデフォルトのオーディオ レンダラである デフォルト DirectShow デバイス フィルタに接続される。ビデオおよびサブピクチャ ストリームに対しては、DVD ナビゲータに続くフィルタとしてサードパーティ製ビデオ デコーダ、オーバーレイ ミキサー、およびビデオ レンダラが接続される。アプリケーションが Line 21 クローズド キャプション データを処理する場合には、DirectShow Line 21 デコーダ フィルタをグラフに追加する必要がある。これには 1 つのメソッド呼び出しが含まれる。このフィルタはビデオ デコーダとオーバーレイ ミキサーの間に自動的に接続される。

アプリケーションは、DVD ナビゲータが公開するカスタム インターフェイスを通じて、DVD ナビゲータと通信し、それを制御する。これは IDvdControl2 ("set" メソッド) と IDvdInfo2 ("get" メソッド) である。また、アプリケーションはグラフの停止や開始などの制御を行うために、フィルタ グラフ マネージャと (IMediaControl を通じて) 通信する必要がある。さらに、場合に応じて、ウィンドウ表示と全画面表示を切り替えるためのオーバーレイ ミキサー フィルタなど、他の個別のフィルタも制御する必要がある。詳細については、「IMixerPinConfig2」を参照すること。グラフの厳密な構成は、インストールされている MPEG-2 デコーダの種類、Line 21 クローズド キャプション データを処理する必要があるかどうかなど、いくつかの条件によって異なる。

DVD フィルタ グラフの作成

DVD 再生フィルタ グラフを作成する方法としては、DVDGraphBuilder オブジェクトによって自動的に行うことを推奨する。このアプローチは、以下の説明、および DVD サンプル アプリケーションで示されている。DVD フィルタ グラフを手動で作成する必要がある場合は、DirectShow に関する他の文書に記載されているグラフ作成の基本的な規則に従って作成することができる。一般に、DVDGraphBuilder によって作成されたグラフに対して個々のフィルタを手動で追加、削除、接続、接続解除することは推奨されない。そのような操作をすると、クリーンアップ コードによる処理で混乱が生じる可能性がある。

DVDGraphBuilder オブジェクトのインスタンスを作成すれば、アプリケーションは単にその IDvdGraphBuilder::RenderDvdVideoVolume メンバ関数を呼び出せばよい。この関数は、ローカル システムに存在する DirectShow フィルタと MPEG-2 デコーダからフィルタ グラフを作成する。詳細については、「フィルタ グラフの構築」を参照すること。

フィルタ グラフが作成されると、アプリケーションは DVD ナビゲータ、フィルタ グラフ マネージャ、およびビデオ ウィンドウの制御に必要なポインタを取得できる。以下のコード例には、エラー チェックなどのコードを省いて単純化した基本的な手順を示している。完全なコードは、DirectX8 SDK DXF\samples\multimedia\dshow\dvd ディレクトリにある DirectShow DVDSample アプリケーションで CDvdCore::BuildGraph() メソッドに含まれている。

   // DVD Graph Builder オブジェクトのインスタンスを作成する。
   HRESULT hr;
   hr = CoCreateInstance(CLSID_DvdGraphBuilder,
                          NULL,
                          CLSCTX_INPROC_SERVER,
                          IID_IDvdGraphBuilder,
                          reinterpret_cast<void**>(&m_pIDvdGB));
   // DVD フィルタ グラフを作成する。
   AM_DVD_RENDERSTATUS	buildStatus;
   hr = m_pIDvdGB->RenderDvdVideoVolume(pszwDiscPath, m_dwRenderFlags, &buildStatus);
   // DVD ナビゲータ インターフェイスへのポインタを取得する。
   hr = m_pIDvdGB->GetDvdInterface(IID_IDvdInfo2, reinterpret_cast<void**>(&m_pIDvdI2));
   hr = m_pIDvdGB->GetDvdInterface(IID_IDvdControl2, reinterpret_cast<void**>(&m_pIDvdC2));
   ...	
   // フィルタ グラフ マネージャへのポインタを取得する。
   hr = m_pDvdGB->GetFiltergraph(&m_pGraph) ;
   ...   
   // グラフ ポインタを使用して IMediaControl へのポインタを取得する。
   // これはフィルタ グラフを全体として制御するために使用する。
   hr = m_pGraph->QueryInterface(IID_IMediaControl, reinterpret_cast<void**>(&m_pIMC));
   ...   
   // IMediaEventEx へのポインタを取得する。
   // これは DVD およびその他のフィルタ グラフ イベントの処理に使用される。
   hr = m_pGraph->QueryInterface(IID_IMediaEventEx, (LPVOID *)&m_pME) ; 
   ...                
   // 再びグラフ ビルダ ポインタを使用して IVideoWindow インターフェイスを取得する。
   // これはビデオ レンダリング フィルタのウィンドウ スタイルとメッセージ処理動作の設定に使用される。
   hr = m_pIDvdGB->GetDvdInterface(IID_IVideoWindow, reinterpret_cast<void**>(&m_pIVW));
   hr = m_pDvdGB->GetDvdInterface(IID_IAMLine21Decoder, (LPVOID *)&pL21Dec) ;

DVD イベント通知の処理

DVD ナビゲータは、特定のイベントが発生したときに、アプリケーションで指定されたウィンドウに通知を送信する。これは、DVD ドメインが変更された場合、新しいペアレンタル ロック レベルを検出した場合、および DVD ナビゲータがアングル ブロックに入る場合などである。イベント パラメータには、イベントに関する追加情報を含むことができる。エラー メッセージや警告も同じ方法で送信される。アプリケーションは、IMediaEventEx ポインタを使って SetNotifyWindow を呼び出すことで、イベント通知を処理するウィンドウを次のように指定する。

    hr = m_pIME->SetNotifyWindow(reinterpret_cast<OAHWND>(m_hWnd), WM_DVD_EVENT, 0);

上記の例において、m_hWnd はメッセージを受信するウィンドウの HWND であり、WM_DVD_EVENT は DVD イベントの発生を示すアプリケーション定義メッセージ (WM_USER より大きい) である。イベント自体は、アプリケーションで IMediaEvent::GetEvent を呼び出すことで取得される。特定の時点でイベント キュー内に複数のイベントが存在する場合があるため、アプリケーションはキュー内のすべてのイベントが取得されるまで繰り返すループの中で GetEvent を呼び出す必要がある。これを次のコード例に示す。

while (SUCCEEDED(m_pIME->GetEvent(&lEvent, &lParam1, &lParam2, lTimeOut)))
	{
        HRESULT hr;
		switch(lEvent)
        {

        case EC_DVD_CURRENT_HMSF_TIME:
        {
            DVD_HMSF_TIMECODE * pTC = reinterpret_cast<DVD_HMSF_TIMECODE *>(&lParam1);
            m_CurTime = *pTC;
            ...
        }
        break;
		...
	}

DVD イベントでは、上記のように lParam1 または lParam2 パラメータに追加情報が含まれる場合がある。ここでは lParam1 に現在の時刻が含まれている。このコード例は Dvdcore.cpp の DVD サンプル アプリケーションから取ったものである。DVD イベントとそのパラメータの完全なリストは、「DVD イベント通知コード」を参照すること。

DVD メニューの操作

DVD メニューの論理的な構造は、「DVD 操作の基礎」で説明している。アプリケーションは、IDvdControl2::ShowMenu を呼び出して表示したいメニューのインデックスを渡すことにより、ディスクからメニューを表示できる。

ボタンを選択したときには、単にその境界線が強調表示されるだけであることに注意する。ボタンのコマンドを実行するには、ボタンをアクティブにする必要がある。IDvdControl2 メソッドの中には、指定されたボタンを選択するものと、ボタンをアクティブにするものとがある。SelectAndActivateButton は、その両方を行う。DVD サンプル アプリケーションの CDvdCore::OnMouseEvent では、マウスでメニュー ボタンを選択してアクティブにする方法が示されている。

アプリケーションでは、IDvdControl2 のメニュー関連メソッドを使用してメニューをプログラム的に制御できる。アプリケーションは、マウスの移動などのユーザーの動作や、その他のプログラム ロジックに基づいて、ボタンを強調表示 (選択) することができる。また、DVD のリモコン上のボタンを表すカスタム メニューまたはビットマップを実装して制御することもできる。DVD サンプル アプリケーションでは、相対ボタン メニュー コマンドがキーボードの矢印キーに関連付けられている (CDvdCore::OnKeyEvent を参照すること)。ボタンをプログラム的にアクティブにするにはさまざまな方法があるが、アクティブにする前に必ずそのボタンを選択する必要がある。

オーディオ ストリームとサブピクチャ ストリーム

DVD-Video ディスクには、最大で 8 つのオーディオ ストリームを含むことができる。これには 0 から 7 までの番号が付けられ、それぞれ 6 つまでの独立したチャンネルを持つ (オーディオ ストリームとサブピクチャ ストリームは番号が 0 から始まるが、タイトル、アングル、およびペアレンタル ロック レベルは 1 から始まることに注意)。これらのうち、同時に選択できるのは 1 つのストリームだけである。サブピクチャの場合は、最大で 32 個のストリームが使用できるが、同時にアクティブにできるのは 1 つだけである。一般に、ディスクはデフォルトのオーディオおよびサブピクチャ ストリームで作成されているが、アプリケーションによって、ユーザーに使用可能なストリームのリストを表示して、希望する言語のストリームを選択させることもできる。このプロセスにおける基本的な手順は、オーディオ ストリームの場合もサブピクチャ ストリームの場合も同じである。

  1. タイトルに対して使用可能なストリームの数を決定する。
  2. 各ストリームを順番に見て、それぞれのストリーム属性を取得する。
  3. 返されたロケール識別子 (LCID) から言語コードを取得して、人間が読める文字列を作成する。
  4. ユーザーが希望のストリームを選択できるように、リスト ボックスやその他のユーザー インターフェイス (UI) コントロールを設定する。

DVD サンプル アプリケーションでは、Dialogs.cpp の CAudioLangDlg::MakeAudioStreamList メソッドに基本的な手順が示されている。

ペアレンタル ロック レベルの適用

DVD-Video ディスクのタイトルやタイトルの一部には、一般的なペアレンタル ロック レベル (PML) 1 から 8 までを割り当てることができる。PML が割り当てられたコンテンツを DVD ナビゲータが読み取る場合、"ペアレンタル ブロック" がかかっていると表現する。1 つのペアレンタル ブロックが、チャプターの一部、複数のチャプター、または複数のタイトルから構成される場合もある。国際市場に向けて作成される DVD アプリケーションでは、そのペアレンタル ロック ロジックに特定のレーティング システムをハード コードすべきではない。

DVD ナビゲータ自体は PML を適用することはない。単に、ディスク上で PML 情報を検出したときにアプリケーションに通知するだけである。デフォルトでは、DVD ナビゲータはディスク上のこの情報を無視し、最高レベルのコンテンツを再生する。PML を適用するには、アプリケーションが何らかの形のパスワード制御ロジックを実装してユーザーとレベルを関連付け、PML イベント通知を送信するように DVD ナビゲータに指示して (起動時に DVD_NotifyParentalLevelChange と TRUE のパラメータで IDvdControl2::SetOption を呼び出す)、このイベントに応答することでアクセスの可否を決定する必要がある。

DVD タイトルには全体で 1 つの PML を割り当ててもよいが、ディスク作成者はそのタイトルの特定の部分に高い (制限の強い) PML を割り当てることもできる。これをテンポラリ PML コマンドと呼ぶ。このようなコマンドには、常に 2 つの分岐命令が含まれる。1 つは、テンポラリ PML コマンドがプレーヤー アプリケーションによって受け入れられた場合に使用され、もう 1 つはコマンドが拒否された場合に使用される。イベントのシーケンスは次のようになる。DVD ナビゲータがビデオ コンテンツ (DVD タイトル ドメイン) の読み取り中にディスク上にテンポラリ PML コマンドを検出する。DVD ナビゲータはその内部フラグをチェックして、アプリケーションがこのイベントの通知を要求しているかどうかを調べる。フラグが設定されていなければ、DVD の再生を続け、ディスク上に指定された "ペアレンタル ロック レベル変更拒否" の分岐をたどる。フラグが設定されている場合は、DVD はアプリケーションに EC_DVD_PARENTAL_LEVEL_CHANGE イベントを送信して、応答が返るまでポーズ状態で待つ。アプリケーションは、イベントを受信すると、独自のロジックを使用してそのコマンドを受け入れるかどうかを決定する。そして、TRUE または FALSE の引数で IDvdControl2::AcceptParentalLevelChange を呼び出す。TRUE の場合、DVD ナビゲータは再生を再開し、ディスク上に指定された "ペアレンタル ロック レベル変更受け入れ" の分岐をたどる。FALSE の場合は、再生を再開し、"ペアレンタル ロック レベル変更拒否" の分岐をたどる。

DvdState オブジェクトの保存と復元

DvdState オブジェクトにより、アプリケーションはユーザー セッションのスナップショットを保存することができる。これには、ディスク上の現在位置、視聴者のペアレンタル ロック レベル、選択されているオーディオおよびサブピクチャ ストリームなどの情報が含まれる。このことは、ユーザーが DVD-Video ディスク上の現在位置を保存して、後でそこから見続けられることを意味する。

アプリケーションで DvdState オブジェクトを作成することはできない。このオブジェクトは、アプリケーションが IDvdInfo2::GetState を呼び出したときに DVD ナビゲータが内部で自動的に作成する。DvdState オブジェクトは、IDvdState インターフェイスを公開して、アプリケーションが特定の情報を照会できるようにする。

DVD サンプル アプリケーションでは、CDvdCore::RestoreBookmark および CDvdCore::SaveBookmark 関数に DvdState オブジェクトの保存および復元方法が示されている。

DVD テキスト文字列の操作

DVD ディスク、特にカラオケ ディスクには、ビデオまたはオーディオ コンテンツを補足するテキスト情報のデータベースが含まれている場合がある。カラオケ ディスク上のそのようなテキストとしては、曲名、アーティスト名、レコード レーベルなどがある。このテキストは、複数の言語で存在することができる。このような文字列はオプションであり、ディスク上に必ず含めなければならないわけではない。文字列が含まれる場合は、DVD ボリュームの論理的な階層を厳密に反映するように構成される。各文字列の前には、その文字列がディスク構造のどの部分に属するかを識別し、文字列の内容の手がかりとなるような数字が付加される。文字列の型の一部が DVD_TextStringType 列挙に定義されている。

テキスト文字列には、基本的な型として構造識別子と内容文字列の 2 つがある。0x01 〜 0x20 の値を持つ型が構造識別子である。これは空の文字列であり、数値コードはその後に続く内容文字列が属する論理構造を識別するために使用される。この構造は、DVD ディスクの内容 (ボリューム、タイトル、チャプターなど) の論理構造とかなり厳密に対応している。もう一方の型は、ユーザー インターフェイスで視聴者に表示される情報を保持する内容文字列を識別する。内容文字列の使用方法は厳密には定義されていないため、DVD 作成者はさまざまな方法でそれを使用できる。

これまで、DVD テキスト文字列はほとんどカラオケ ディスク専用に使用されてきた。これらのディスクは、ほとんどの場合、0x01 と 0x02 の構造識別子と 0x30 の内容文字列を使用している。しかし、時がたつにつれて、より多くの種類の DVD-Video ディスクにテキスト文字列が含まれるようになると考えるのが妥当である。これらの文字列は、より複雑に構成され、ディスク内容の詳細な記述を提供するようになるであろう。

DVD テキスト文字列については、DVD フォーラムの Web サイト (www.dvdforum.org) を参照すること。

DVD サンプル アプリケーションの CDvdCore::GetDvdText メソッドに、DVD テキスト文字列を列挙して表示する基本的な手順が示されている。

カラオケ オーディオ ストリームの再生

DVD ナビゲータは、カラオケ オーディオ ストリームを含む DVD-Video ディスクを再生できるが、カラオケの再生にはマルチチャンネル カラオケ ミキシングをサポートするデコーダも必要である。具体的に言えば、このデコーダは DVD カラオケ プロパティ セット (AM_KSPROPSETID_DvdKaraoke) をサポートする必要がある。

カラオケ ディスクは DVD-Video ディスクの一種であり、同じ操作構造を持っている。曲は一般にタイトルとしてフォーマットされ、タイトルは演奏者や音楽スタイルなどの条件に基づいてタイトル セットにグループ化できる。カラオケ ディスクと他の種類の DVD-Video との主な違いは、オーディオ ストリームである。すべてのカラオケ ディスクには、マルチチャンネル オーディオ (通常は Dolby AC-3) が含まれている。チャンネル 0 および 1 には常にバックグラウンドの演奏が含まれ、チャンネル 2 〜 5 にはそれぞれガイド ボーカル、ガイド メロディ、サウンド エフェクトの任意の組み合わせを含むことができる。カラオケ アプリケーションは、各補助チャンネルについてボリュームと出力先スピーカを制御できる。

DVD ナビゲータはディスク上にカラオケ コンテンツを検出してカラオケ モードに入ると、デコーダにそれを通知し、デコーダはアプリケーションによって明示的にオンにされるまで上位 3 つのチャンネル (補助チャンネル) を無音状態にする。カラオケ アプリケーションの基本的な動作は次のとおりである。

  1. IDvdInfo2 メソッドを使用して補助チャンネルの数とその内容を確認する。
  2. IDvdControl2::SelectKaraokeAudioPresentationMode を使用して、チャンネルの内容を表示してユーザーがいつでも任意の補助チャンネルをオンまたはオフにできるようにするユーザー インターフェイスを提供する。

これらの手順は、DVD サンプル アプリケーションの DVDCore.cpp の GetAudioAttributes メソッドに示されている。

DVD コマンドの同期化

ここには、以下のサブトピックが含まれる。

コマンドの同期化について

コマンドの同期化は、DVD ナビゲータおよび一般的な DVD の操作に精通した開発者のための高度なトピックであり、アプリケーションでコマンドの同期化による洗練された機能を必要とする場合に役立つ。まだ DVD ナビゲータを使い始めたばかりである場合や、アプリケーションでコマンドの同期化を必要としない場合には、単にアプローチ 1 に示される各種の再生コマンドを使用する。コマンドの同期化は、後からいつでも導入することができる。

IDvdControl2 の各種の再生メソッド (PlayTitle など) は、本質的に非同期である。メソッドが呼び出されると、必要なすべての手順を実行する前に、メソッドは直ちに結果を返す。メソッド呼び出しが正常に結果を返してからその動作が最終的に完了するまでの間、他のコマンドが実行されて DVD ナビゲータが新しい DVD ドメインまたは新しいフィルタ状態に入り、メソッドがすべての必要な手順を正常に完了できない場合がある。たとえば、アプリケーションが PlayTitle を呼び出して、その後すぐにユーザーが停止ボタンをクリックしたとする。コマンドが同期化されていないと、DVD ナビゲータは、ディスクが指定された位置で再生を開始する前に DVD 停止ドメインに入る場合がある。これは、全体の動作が実行される前に PlayTitle が正常に結果を返すためである。最終的にディスクが再生を開始したときに、DVD ナビゲータが既に正しい状態でなくなっていて、エラーが生じる場合がある。

多くのメソッド呼び出しが特定の状態の DVD ナビゲータに依存しているため、アプリケーションはコマンドを同期化する方法を必要とする。これには次の 2 つの基本的な機能が含まれる。

DVD ナビゲータはアプリケーションにこれらの機能を提供し、コマンドの同期化のためのいくつかのオプションを提供する。IDvdControl2 の各再生メソッドには同期化のためのパラメータが 2 つある。1 つは同期化オブジェクトへのインターフェイス ポインタであり、もう 1 つは DVD コマンド フラグのセットである。アプリケーションはこれらのパラメータを使用して、コマンドの同期化を必要に応じたさまざまな方法で実装できる。

アプローチ 1 : 同期化なし

メソッド呼び出しの非同期の性質を保持するには、次の構文を使用する。ここで、pDVDControl2IDvdControl2 ポインタである。

	
HRESULT hres = pDVDControl2->PlayTitle( uTitle, 
                                        DVD_CMD_FLAG_None, // = 0
                                        NULL);

アプローチ 2 : 同期化なしの単純なブロッキング

このアプローチでは、DVD コマンド フラグを使用して、コマンド同期化オブジェクトを管理しない単純なブロッキングを実現する。これは、同期化オブジェクトを管理したりイベント通知を処理する必要がないため、最も簡単なコマンドの同期化方法であるが、特定のコマンドの戻り状態を決定することはできない。

HRESULT hres = pDVDControl2->PlayTitle( uTitle,
                                        EC_DVD_CMD_FLAG_Block,         
                                        NULL);

アプローチ 3 : 同期化オブジェクトのみを使用

このアプローチは、機能的にはアプローチ 2 と等価である。イベント通知を処理せずに直接 DVD ナビゲータをブロックする別の方法を提供する。アプリケーションが IDvdCmd ポインタのアドレスを渡すと、この特定のコマンドに関連するコマンド同期化オブジェクトを受け取る。オブジェクトの WaitToEnd 関数を呼び出すと、メソッドが正常に完了するまで DVD ナビゲータがブロックされる。その間、新しいコマンドがキューに追加される場合がある。オブジェクトは、使い終わったら必ず解放しなければならない。

IDvdCmd* pObj;
HRESULT hr = pDVDControl2->PlayTitle( uTitle,  0, &pObj);
if(SUCCEEDED(hr))
{
    pObj->WaitToEnd();
    pObj->Release();
}

アプローチ 4 : イベントを使用した一度に 1 コマンドの同期化

IDvdControl2::SetOption の呼び出しで EC_DVD_CMD_FLAG_SendEvents フラグを指定することにより、DVD ナビゲータは "再生" メソッド呼び出しに対してはブロックされない。その代わり、コマンドの開始時、およびその非同期動作の全体が完了したときに、アプリケーションに EC_DVD_CMD_START および EC_DVD_CMD_END イベント通知を送信する。これにより、IDvdCmd オブジェクトを管理せずに単一のイベントを同期化することができる。しかし、IDvdCmd オブジェクトを使用しないため、イベントがどのコマンドに関連するものかを正確に知ることはできない。多くの状況においては、そのような情報は必要とされないため、このアプローチで十分である。

// "SendEvents" を使用して PlayChapterInTitle を呼び出す。
HRESULT hres = pDVDControl2->PlayChapterInTitle( uTitle, 0x2,
                    DVD_CMD_FLAG_SendEvents, NULL);
…
// イベント通知ハンドラ関数において
switch (lEvent)
{   
   case EC_DVD_CMD_END:
	 DoSomethingGeneric(); 
	 break;
}
     

アプローチ 5 : イベントと IDvdCmd オブジェクトを使用した同期化

このアプローチは、コマンドの同期化について最高の制御を得ることができるが、実装が最も複雑なアプローチでもある。アプリケーションは、イベントがどのコマンドに関連付けられているかを知ることができるため、EC_DVD_CMD_END イベントへの応答を適宜調整できる。最初の例にシングルスレッドのソリューションを示し、2 番目の例にマルチスレッドのソリューションを示す。

// グローバル コードにおいて
IDvdCmd* pGlobalObj=0;
…
// pGlobalObj はイベントが発行される "前" に Navigator によって
// 割り当てられる。そうしないと、pGlobalObj が割り当てられる前の
// (*1) でイベントが発生する可能性がある。
HRESULT hres = pDVDControl2->PlayTitle( uTitle,
DVD_CMD_FLAG_SendEvents, &pGlobalObj );
// (*1)
If( FAILED( hres)) {
pGlobalObj = NULL;
}
…
switch (lEvent)
{
case EC_DVD_CMD_END:
   IDvdCmd* pObj = GetDvdCmd( lParam1 );
   HRESULT hres = HRESULT( lParam2 );
If( NULL != pObj ) {
	If(pGlobalObj == pObj ) {
…
pGlobalObj->Release();
pGlobalObj = NULL;
}
pObj->Release();
   }
break;

このアプローチは、イベント ループが別のスレッドに存在するときに使用する。

// グローバル コードにおいて
IDvdCmd* pGlobalObj=0;
CcritSect globalCritSect;
…
{
CautoLock(globalCritSect );
HRESULT hres = pDVDControl2->PlayTitle( uTitle,
DVD_CMD_FLAG_SendEvents, &pGlobalObj );
If( FAILED( hres)) {
pGlobalObj = NULL;
}
}
…
switch (lEvent)
{
case EC_DVD_CMD_COMPLETE:
case EC_DVD_CMD_CANCEL:
{
CautoLock(globalCritSect );
IDvdCmd* pObj = GetDvdCmd( lParam1 );
HRESULT hres = HRESULT( lParam2 );
If( NULL != pObj ) {
If(pGlobalObj == obj ) {
…
pGlobalObj->Release();
pGlobalObj = NULL;
}
pObj->Release();
}
break;
}

DVD コマンド フラグ

SetOptiondwFlags パラメータに渡される DVD コマンド フラグは、同期化オブジェクトに加えて、または同期化オブジェクトの代わりに使用される。これは、特定のメソッド呼び出しに対してどのアプローチを使用するかによって異なる。DVD_CMD_FLAG_Block の使用はアプローチ 2 に示され、DVD_CMD_FLAG_SendEvents はアプローチ 4 〜 6 に示されている。これらのフラグはいずれも、他のフラグ DVD_CMD_FLAG_Flush とビット単位の OR で結合することもできる。このフラグは、メソッドが直ちに実行され、フィルタ グラフのバッファ内の 1 〜 2 秒のビデオが再生されるのを待たないようにする。このフラグを指定すると、ユーザー入力への応答時間が短くなる。次のコマンドが実行される前に特定のビデオ セグメントを完全に終了させる必要がある場合には、このフラグは使用しない。4 つのフラグとそれぞれの意味を次の表に示す。

名前数値意味
DVD_CMD_FLAG_None0x00000000フラグなし。次のコマンドの前にバッファをフラッシュせず、動作の完了時にイベントを発行せず、DVD ナビゲータはメソッドが結果を返した後にブロックされない。
DVD_CMD_FLAG_Flush0x00000001バッファを空にして、新しい位置から直ちに再生を開始する。
DVD_CMD_FLAG_SendEvents0x00000002アプリケーションにイベントを送り返すことで非同期動作の完了とその戻り値を通知するように、DVD ナビゲータに指示する。
DVD_CMD_FLAG_Block0x00000004非同期動作が完了するか取り消されるまで DVD ナビゲータをブロックする。

ディスクの取り出し処理

ユーザーが DVD をドライブから取り出すと、DVD ナビゲータはアプリケーションに EC_DVD_DISC_EJECTED メッセージを送信する。アプリケーションはこのメッセージを無視しても問題なく、DVD ナビゲータにグラフの状態を管理させる。また、アプリケーションは、EC_DVD_DISC_EJECTED メッセージを処理して、ディスクの取り出し時にカスタム動作を実現することもできる。メッセージを処理する場合は、SetOption メソッドで DVD_ResetOnStop フラグを TRUE に設定してから、アプリケーションを閉じるか他のディスクを再生する前に、IMediaControl::Stop を呼び出す必要がある。