Microsoft DirectX 8.0

キャプチャ アプリケーションの書き方

このトピックでは、Microsoft® DirectShow® を使ってビデオ キャプチャ アプリケーションを書く方法を説明する。以下のセクションで構成される。

DirectShow でのキャプチャの概要

"キャプチャ"という用語は、ファイルへの書き込みの意味があるが、キャプチャしたデータのプレビューやその他の使用方法の意味も含んでいる。キャプチャはビデオ カメラに限定されるものではなく、テレビ チューナーなどもキャプチャ デバイスである。ここでは、オーディオおよびビデオのキャプチャについて説明するが、キャプチャ データには、クローズド キャプションや垂直同期間隔 (VBI) のデータが含まれる場合があることを覚えておいてほしい。

キャプチャ グラフ

DirectShow は、ほかのタスクと同様にフィルタ グラフを使ってキャプチャを処理する。この種類のグラフは "キャプチャ グラフ" と呼ばれる。さまざまなハードウェア デバイスやデータ フォーマットを利用できるため、キャプチャ グラフはいろいろな方法で設定できる。ほとんどのキャプチャ グラフには、以下のような共通点がある。

次の図は、典型的なキャプチャ グラフでのフィルタの種類を示す。波線は、デコーダ フィルタやスプリッタ フィルタなどの追加フィルタが必要になるかもしれない部分を示している。

キャプチャ グラフ

キャプチャ グラフの作成を簡単にするために、DirectShow はキャプチャ グラフ ビルダというコンポーネントを提供している。このコンポーネントが公開する ICaptureGraphBuilder2 インターフェイスが、キャプチャ グラフを作成して制御するためのメソッドを持っている。このトピックでは、キャプチャ アプリケーションでキャプチャ グラフ ビルダを使用することを前提にしている。

キャプチャ フィルタ

キャプチャ フィルタは、1 つ以上の出力ピンを通じてデータを配信する。出力ピンは、配信する"メディア タイプ"と"ピン カテゴリ"によって分類できる。

メディア タイプは、メジャー タイプ GUID によって表される。最も共通のメディア タイプは以下の表のとおりである。

メディア タイプメジャー タイプ GUID
オーディオMEDIATYPE_Audio
インターリーブされたデジタル ビデオ (DV)MEDIATYPE_Interleaved
MPEG ストリームMEDIATYPE_Stream
ビデオMEDIATYPE_Video

ピン カテゴリはピンの目的と機能を示すもので、プロパティ セット GUID で表される。ピン カテゴリはいくつかあるが、ここでは以下の 2 つについて説明する。

すべてのピン カテゴリについては、「ピン プロパティ セット」を参照すること。

メディア タイプとピン カテゴリを、ピンに直接問い合わせることができる。メディア タイプを取得するには IPin::EnumMediaTypes メソッドを呼び出し、ピン カテゴリを取得するには IKsPropertySet::Get メソッドを呼び出す。ただし、ICaptureGraphBuilder2 メソッドを使用すれば、各ピンに問い合わせなくても、特定のメディア タイプまたはピン カテゴリに操作を限定することができる。

キャプチャ グラフの作成

キャプチャ グラフは以下の手順で作成する。

  1. 必要なコンポーネントの作成
  2. キャプチャ デバイスの選択
  3. ファイル書き込みセクションの作成
  4. ストリームのレンダリング

必要なコンポーネントの作成

キャプチャ グラフの作成では、まず空のフィルタ グラフとキャプチャ グラフ ビルダを作成する。次に、ICaptureGraphBuilder2::SetFiltergraph を呼び出して、フィルタ グラフをキャプチャ グラフ ビルダに関連付ける。以下のコードにこれらの手順を示す。

IGraphBuilder *pGraph = NULL;
ICaptureGraphBuilder2 *pBuilder = NULL;

// フィルタ グラフを作成する。
CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC,
    IID_IGraphBuilder, (void **)&pGraph);

// キャプチャ グラフ ビルダを作成する。
CoCreateInstance(CLSID_CaptureGraphBuilder2, NULL, CLSCTX_INPROC, 
    IID_ICaptureGraphBuilder2, (void **)&pBuilder);

pBuilder->SetFiltergraph(pGraph);    

キャプチャ デバイスの選択

2 つめのステップは、システム デバイス 列挙子を使用したキャプチャ デバイスの選択である。詳細については、「システム デバイス列挙子の使用」を参照すること。以下のコードは、ビデオ キャプチャ デバイスを列挙し、列挙シーケンス内の最初のデバイスを選択する。

// システム デバイス列挙子を作成する。
ICreateDevEnum *pDevEnum = NULL;
CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC, 
    IID_ICreateDevEnum, (void **)&pDevEnum);

// ビデオ キャプチャ デバイスの列挙子を作成する。
IEnumMoniker *pClassEnum = NULL;
pDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, &pClassEnum, 0);

ULONG cFetched;
IMoniker *pMoniker = NULL;
IBaseFilter *psrc = null;
if (pClassEnum->Next(1, &pMoniker, &cFetched) == S_OK)
{
    // 最初のモニカをフィルタ オブジェクトにバインドする。
    pMoniker->BindToObject(0, 0, IID_IBaseFilter, (void**)&pSrc);
    pMoniker->Release();
}
pClassEnum->Release();
pDevEnum->Release();

一般的には、使用可能なデバイスの名前が入ったリスト ボックスを表示して、ユーザーにデバイスを選択させるのが普通だろう。そうするには、IMoniker::BindToStorage メソッドを呼び出して、デバイス モニカのプロパティ バッグを取得し、IPropertyBag::Read メソッドを呼び出して、デバイスのフレンドリ名を取得する。詳細については、「システム デバイス列挙子の使用」を参照すること。

ここで、以下のようにキャプチャ フィルタをグラフに追加する。

// pSrc は、前のコードのキャプチャ フィルタである。
pGraph->AddFilter(pSrc, L"Video Capture");

注 :  オーディオとビデオの両方をキャプチャするデバイスは、[Audio Capture Sources] と [Video Capture Sources] の 2 つのカテゴリに表示される。一方のカテゴリからデバイスを選択した後、フィルタの出力ピンのメディア タイプをチェックする。デバイスが両方のカテゴリに分類される場合は、2 つめのデバイスを追加してはいけない。これは、インターリーブされたオーディオ/ビデオを生成するデジタル ビデオ (DV) カメラの場合にも当てはまる。

ファイル書き込みセクションの作成

ファイル キャプチャを実行するために、フィルタ グラフにファイル書き込みセクションを追加する(プレビュー機能だけが必要な場合は、このステップを飛ばしてよい)。ICaptureGraphBuilder2::SetOutputFileName メソッドを使用する。このメソッドは、必要な mux フィルタとファイル書き込みフィルタをグラフに追加する。このメソッドは、出力ファイルの名前とフォーマットを指定する入力パラメータを受け取るが、フォーマットについては以下のいずれかの値を使用できる。

3 つめのオプションは、サードパーティ製のフィルタを使用するためのものである。たとえば、MPEG データを渡すキャプチャ デバイスがあるが、DirectShow には MPEG ライタ フィルタが付属していないので、MPEG ファイルを書き出すにはサードパーティ製のフィルタが必要になる。

以下の例では、出力ファイルの名前を Example.avi に設定し、フォーマットを AVI に設定している。

IBaseFilter     *ppf = NULL;
IFileSinkFilter *pSink = NULL;
pBuilder->SetOutputFileName(&MEDIASUBTYPE_Avi, L"C:\\Example.avi", &ppf, &pSink);

このメソッドは、戻ってくると "ppf" パラメータをマルチプレクサの IBaseFilter インターフェイスに設定し、"pSink" パラメータをファイル ライタの IFileSinkFilter インターフェイスに設定する。これらは同じフィルタであってもよい。たとえば、ASF ライタ フィルタはファイル書き込みとマルチプレクサ処理の両方を行う。

次の図は、AVI ファイル (左) と ASF ファイル (右) の書き込みに使用するフィルタを示す。

AVI ファイルと ASF ファイルの書き込みに使用するフィルタ

AVI Mux フィルタは IConfigAviMux インターフェイスおよび IConfigInterleaving インターフェイスを公開し、ASF ライタ フィルタは IConfigAsfWriter インターフェイスを公開する。これらのインターフェイスを使って、出力ファイルのフォーマットを設定することができる。ASF ファイルの場合は、ソフトウェア証明書も与えなければならない。詳細については、「DirectShow での ASF ファイルの作成」を参照すること。

注 :  ICaptureGraphBuilder2::AllocCapFile メソッドを使って、キャプチャ ファイルを前もって割り当てておくことをお勧めする。前もって割り当てた大きなファイルにキャプチャすれば、キャプチャのパフォーマンスが向上する。

ストリームのレンダリング

キャプチャ グラフ作成の最終ステップは、グラフ内のストリームのレンダリングである。ファイル キャプチャの場合、キャプチャ ピンは、前に説明したファイル書き込みセクションに接続される。プレビューの場合、プレビュー ピンはオーディオおよびビデオ レンダラに接続される。ただし、キャプチャ フィルタにプレビュー ピンがない場合は、代わりにキャプチャ ピンを使用しなければならない。同じピンをキャプチャとプレビューに使用するためには、キャプチャ ピンはスマート ティー フィルタに接続されていなければならない。このフィルタは、データをキャプチャ ストリームとプレビュー ストリームの 2 つに分離する。

次の図は、スマート ティー フィルタを使用するグラフを示す。

スマート ティー

ICaptureGraphBuilder2::RenderStream メソッドが、これらのタスクをすべて処理し、必要な場合は スマート ティー フィルタを追加する。このメソッドは、WDM デバイスに必要なサポート フィルタも自動的に追加する。以下のコードは、ファイル キャプチャ用のビデオ ストリームをレンダリングする。

pBuilder->RenderStream(
        &PIN_CATEGORY_CAPTURE,  // ピン カテゴリ
        &MEDIATYPE_Video,       // メディア タイプ
        pSrc,                   // キャプチャ フィルタ
        NULL,                   // 圧縮フィルタ (オプション)
        ppf                     // マルチプレクサまたはレンダリング フィルタ
    );

最初の 2 つのパラメータで、ピン カテゴリとメディア タイプを指定する。それに続く 3 つのパラメータは、以下のフィルタへのポインタである。

プレビューのストリームをレンダリングするには、ピン カテゴリに PIN_CATEGORY_PREVIEW を使用し、最後の 2 つのパラメータに NULL を使用する。キャプチャ フィルタにプレビュー ピンがない場合、キャプチャ グラフ ビルダは自動的に スマート ティー フィルタを使用する。以下のコードは、ビデオ プレビュー ストリームをレンダリングする。

pBuilder->RenderStream(
        &PIN_CATEGORY_PREVIEW, 
        &MEDIATYPE_Video, 
        pSrc, 
        NULL,   // 圧縮フィルタなし
        NULL    // デフォルトのレンダラ
    );

レンダリングするメディア タイプごとに、これらの RenderStream への呼び出しを繰り返す。

注 :  DV キャプチャ フィルタの中には、インターリーブされたデータ (MEDIATYPE_Interleaved) 用に 1 つのピンを持ち、ビデオだけのデータ (MEDIATYPE_Video) 用に別のピンを持つものがある。両方ではなく、いずれか一方をレンダリングする。ビデオ専用ピンをレンダリングする場合、キャプチャ データはオーディオを持たない。

グラフ プロパティの設定

グラフ内のさまざまなフィルタやピンは、プロパティを設定および取得するためのインターフェイスを公開している場合がある。ICaptureGraphBuilder2::FindInterface メソッドを使って、それらのインターフェイスへのポインタを取得する。

アプリケーションによって異なるが、以下の表にあるインターフェイスが役に立つと思われる。

IAMAudioInputMixerオーディオ入力プロパティを設定する。
IAMDroppedFramesドロップ フレーム情報を取得する。
IAMVfwCaptureDialogsMicrosoft® Video for Windows® のキャプチャ ドライバ用に提供されるダイアログ ボックスを表示する。
IAMVideoCompression圧縮パラメータを設定する。

以下のコードは、インターリーブされたオーディオ/ビデオ ピンから IAMDroppedFrames インターフェイスを取得する。

IAMDroppedFrames *pDropped = NULL;
pBuilder->FindInterface(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Interleaved, 
    pSrc, IID_IAMDroppedFrames, (void **)&pDropped);

ただし、ビデオ再生ウィンドウを制御するためのメソッドが含まれる IVideoWindow インターフェイスを取得する場合は、FindInterface メソッドを呼び出さない。代わりに、フィルタ グラフにこのインターフェイスの有無を問い合わせる。そうしないと、画面解像度などのイベントにフィルタ グラフが正しく応答しなくなる。IVideoWindow を使用すると、プレビュー ウィンドウをアプリケーション ウィンドウの子ウィンドウにすることができる。詳細については、「ビデオ ウィンドウの設定」を参照すること。

注 :  プレビュー ストリームをレンダリングしないときでも、キャプチャ グラフ ビルダはプレビュー ウィンドウを追加することがある。一部のハードウェア デバイスは、ビデオ キャプチャのためにビデオ ポート エクステンション (VPE) を使用するのでプレビュー ウィンドウが必要になる。そのため、常にフィルタ グラフに IVideoWindow の有無を問い合わせるのがよいだろう。フィルタ グラフがこのインターフェイスを公開している場合は、ビデオ ウィンドウをアプリケーション ウィンドウの子ウィンドウにする。

キャプチャ グラフの制御

フィルタ グラフの IMediaControl インターフェイスには、グラフ全体を実行、停止、およびポーズするためのメソッドがあるが、グラフに複数のストリームがある場合はそれらを独立して制御したいことがある。たとえば、キャプチャ ストリームを停止した状態でプレビュー ストリームを実行する場合や、プレビューの間はオーディオをミュートして、ファイル キャプチャの間は有効にするような場合だ。

ICaptureGraphBuilder2::ControlStream メソッドが、それぞれのストリームの開始タイムと終了タイムを設定する手段を提供する。IMediaControl::Run を呼び出す前に、このメソッドを呼び出す。Run を呼び出すと、ControlStream の呼び出しに含まれないストリームが直ちに開始される。

以下のコードは、ビデオ キャプチャ ストリームを 2 秒の位置から開始し、5 秒の位置で停止する。

IMediaControl   *pControl;
REFERENCE_TIME  rtStart = 20000000, 
                rtStop = 50000000;

pBuilder->ControlStream(
        &PIN_CATEGORY_CAPTURE, 
        &MEDIATYPE_Video, 
        pSrc,       // ソース フィルタ
        &rtStart,   // 開始タイム
        &rtStop,    // 終了タイム
        0,          // 開始クッキー
        0           // 停止クッキー
    );
pGraph->QueryInterface(IID_IMediaControl, (void **)&pControl);
pControl->Run();

最初の 3 つのパラメータで、ピン カテゴリ、メディア タイプ、およびキャプチャ フィルタによって制御するストリームを指定する。最後の 2 つのパラメータは、開始および停止のイベントを識別するために使用できるクッキーだ。クッキーは、アプリケーションで定義する任意の値である。詳細については、「ICaptureGraphBuilder2::ControlStream」を参照すること。

複数のストリームを一度に制御する場合は、メディア タイプとソース フィルタを省略する。以下の例では、グラフ内のすべてのキャプチャ ストリームを制御している。

pBuilder->ControlStream(&PIN_CATEGORY_CAPTURE, NULL, NULL, 
    &rtStart, &rtStop, 0, 0);
pControl->Run();

デバイスの削除

グラフが使用していたプラグ アンド プレイ デバイスをユーザーが削除すると、フィルタ グラフ マネージャが EC_DEVICE_LOST イベントを発行する。そのデバイスが再び使用可能になると、フィルタ グラフ マネージャはもう一度 EC_DEVICE_LOST イベントを送信する。しかし、キャプチャ グラフの前の状態は正しくなくなる。アプリケーションは、デバイスを使用するためにグラフを再作成しなければならない。詳細については、「DirectShow でのイベント通知」を参照すること。