DVカメラ映像を表示

このページの目標

DirectShow を使って、DV カメラ の映像を表示するアプリケーションを作成します。

DirectShow における DV カメラの扱い

DirectShow における DV カメラ

DirectShow においては、DV カメラも一つのフィルタとして表されます。MSDV ドライバといわれる DV カメラ用のドライバがあり、それがフィルタとして表されます。

※ 標準解像度の dvsd (NTSC もしくは PAL)のみサポートされます。その他のフォーマットは標準で処理を行うフィルタが用意されていないため扱うことができません。

画面への表示手順

MSDV ドライバのフィルタをフィルタグラフへ追加します。

このフィルタがソースフィルタとなりますので、出力ピンから映像と音声を取り出しレンダリングします。映像は Video Renderer, 音声は Default DirectSound Device へ接続します。

このようなフィルタグラフを作成します。

ソースコードを組んでみる

プロジェクトの作成

まずは、C++ 言語でフィルタグラフの作成・制御を参照し、基本的な設定をしてください。

基本的な部分

DVCamViewer.cpp を新規作成します。

インターフェイスを記憶しておく変数を宣言します。解放する関数 ReleaseInterfaces を実装しておきます。

今回は、 CoCreateInstance 関数の呼び出しを簡略化するために CREATE_COM_INST マクロを定義しました。

#include "stdafx.h"

#define SAFE_RELEASE(p) { if((p)!=NULL) { (p)->Release(); (p)=NULL; }}
#define FAILED_RELEASE(r) if(FAILED(r)){ printf("%s:(%d) Failed. HRESULT=%x\n",__FILE__,__LINE__,hr); ReleaseInterfaces(); return 0; }
#define CREATE_COM_INST(clsid,iid,obj) CoCreateInstance(clsid, NULL, CLSCTX_INPROC_SERVER, iid,reinterpret_cast<void **>(obj))

ICaptureGraphBuilder2 *pCapGrph = NULL;
IGraphBuilder *        pGrph = NULL; 
IMediaControl *        pCtrl = NULL;
IBaseFilter *          pDVCam= NULL;		// DVcam を表すフィルタ
IBaseFilter *		   pSplitter  =NULL;
IBaseFilter *		   pRenderer  =NULL;
IBaseFilter *		   pDSRenderer=NULL;
IBaseFilter *		   pDecoder   =NULL;

void ReleaseInterfaces(){
	SAFE_RELEASE(pCapGrph);
	SAFE_RELEASE(pGrph);
	SAFE_RELEASE(pCtrl);
	SAFE_RELEASE(pDVCam);
	SAFE_RELEASE(pSplitter);
	SAFE_RELEASE(pRenderer);
	SAFE_RELEASE(pDSRenderer);
	CoUninitialize();
}

デバイス列挙関数の実装

DV カメラのフィルタは、CoCreateInstance 関数で直接作成することはできません。 PC に接続されている DV カメラを列挙し、そこからフィルタを作成します。

クラス ID CLSID_SystemDeviceEnum , インターフェイス ID IID_ICreateDevEnum で CoCreateInstance 関数を呼び出します。次に ICreateDevEnum::CreateClassEnumerator を呼び出します。これでビデオキャプチャデバイスが列挙可能になります。

IEnumMoniker::Next で列挙します。ビデオキャプチャデバイスごとに IMoniker が得られるので、DV カメラかどうか判定し、IMoniker::BindToObject で IBaseFilter インターフェイスを持つオブジェクトを取得します。DV カメラの判定には、Imoniker::BindToStorage で取得した IPropertyBag で Description プロパティを取得できるかどうかで判定しています。より厳密に判定するには、DV フォーマットを出力するピンがあるかどうかで判定を行ったら良いでしょう。

HRESULT CreateDvCam(){
	ICreateDevEnum *pDevEnum = NULL;
	IEnumMoniker *pEnum = NULL;
	// システム デバイス列挙子を作成する。
	HRESULT hr=CREATE_COM_INST(CLSID_SystemDeviceEnum,IID_ICreateDevEnum,&pDevEnum);
	if(FAILED(hr)) {
		return hr;
	}
	// ビデオ キャプチャ カテゴリの列挙子を作成する。
	hr = pDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory,&pEnum, 0);
	if(hr!=S_OK){
		return hr;
	}
	IMoniker *pMoniker = NULL;
	while (pEnum->Next(1, &pMoniker, NULL) == S_OK && pDVCam==NULL)
	{
		IPropertyBag *pPropBag=NULL;
		hr = pMoniker->BindToStorage(0, 0, IID_IPropertyBag, reinterpret_cast<void**>(&pPropBag));
		if (SUCCEEDED(hr)){
			VARIANT varName;
            VariantInit(&varName);
            hr = pPropBag->Read(TEXT("FriendlyName"), &varName, 0);
            if (SUCCEEDED(hr)){
				wprintf(TEXT("%s\n"),varName.bstrVal);
            }
			// Description を取得できるのは、DV と D-VHS/MPEGのカムコーダデバイスのみ。
			VariantClear(&varName);
            VariantInit(&varName);
            hr = pPropBag->Read(TEXT("Description"), &varName, 0);
            if (SUCCEEDED(hr)){
				wprintf(TEXT("%s\n"),varName.bstrVal);
				// IBaseFilter を取得する
				hr = pMoniker->BindToObject(0, 0, IID_IBaseFilter, reinterpret_cast<void**>(&pDVCam));
			}
			VariantClear(&varName);
		} 
		SAFE_RELEASE(pPropBag);
		SAFE_RELEASE(pMoniker);
	}
	return pDVCam==NULL ? E_FAIL : S_OK;
}

フィルタグラフの構築

フィルタグラフを構築します。

ここでは IGraphBuilder ではなく ICaptureGraphBuilder2 インターフェイスを使ってフィルタグラフを構築しています。 IGraphBuilder より便利なメソッドが用意されているためです。

int _tmain(int argc, _TCHAR* argv[]){
	HRESULT hr=NOERROR;
	IBaseFilter *pBaseF=NULL;
	hr=CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
	//  graph
	FAILED_RELEASE(CREATE_COM_INST(CLSID_CaptureGraphBuilder2, IID_ICaptureGraphBuilder2,&pCapGrph));
	FAILED_RELEASE(CREATE_COM_INST(CLSID_FilterGraph, IID_IGraphBuilder,&pGrph));
	FAILED_RELEASE(pCapGrph->SetFiltergraph(pGrph));
	FAILED_RELEASE(pGrph->QueryInterface(IID_IMediaControl, (void**)&pCtrl));
	// add filter : DV cam
	FAILED_RELEASE(CreateDvCam());
	FAILED_RELEASE(pGrph->AddFilter(pDVCam,TEXT("DVCam")));
	// add filter : DV splitter
	FAILED_RELEASE(CREATE_COM_INST(CLSID_DVSplitter, IID_IBaseFilter,&pSplitter));
	FAILED_RELEASE(pGrph->AddFilter(pSplitter,TEXT("Splitter")));
	// add filter : DV decoder
	FAILED_RELEASE(CREATE_COM_INST(CLSID_DVVideoCodec, IID_IBaseFilter, &pDecoder));
	FAILED_RELEASE(pGrph->AddFilter(pDecoder,TEXT("DV Decoder")));
	// set decoder resolution
	IIPDVDec *pDD=NULL;
	FAILED_RELEASE(pDecoder->QueryInterface(IID_IIPDVDec,(void**)&pDD));
	pDD->put_IPDisplay(DVDECODERRESOLUTION_360x240);
	SAFE_RELEASE(pDD);
	// add filter : video renderer
	FAILED_RELEASE(CREATE_COM_INST(CLSID_VideoRenderer, IID_IBaseFilter, &pRenderer));
	FAILED_RELEASE(pGrph->AddFilter(pRenderer,TEXT("Video Renderer")));
	// add filter : directsound renderer
	FAILED_RELEASE(CREATE_COM_INST(CLSID_DSoundRender, IID_IBaseFilter, &pDSRenderer));
	FAILED_RELEASE(pGrph->AddFilter(pDSRenderer,TEXT("DirectSound Renderer")));

 

ICaptureGraphBuilder2::RenderStream を使ってフィルタを接続

ICaptureGraphBuilder2::RenderStream を使ってフィルタを接続します。このメソッドは、接続開始フィルタ、中間フィルタ、終端フィルタの 3 つの引数を指定し、インテリジェント接続で自動的にフィルタを接続していくれる便利なメソッドです。

ピンカテゴリや、メジャーメディアタイプを指定できるので複数の出力ピンがあるフィルタでも選択が可能です。

	FAILED_RELEASE(pCapGrph->RenderStream(&PIN_CATEGORY_CAPTURE ,&MEDIATYPE_Interleaved,pDVCam,NULL,pSplitter));
	FAILED_RELEASE(pCapGrph->RenderStream(NULL,&MEDIATYPE_Video,pSplitter,pDecoder,pRenderer));
	FAILED_RELEASE(pCapGrph->RenderStream(NULL,&MEDIATYPE_Audio,pSplitter,NULL,pDSRenderer));

フィルタグラフの制御

IMediaControl::Run メソッドでフィルタグラフを実行します。_getch 関数を呼んで、任意のキーが押されるとフィルタグラフを停止するようにします。

	pCtrl->Run();
	_getch();
	pCtrl->StopWhenReady();
	ReleaseInterfaces();
	return 0;
}

 

実際に動かすには

実際に動かす前に、DV カメラをカメラモードにして電源を入れて IEEE1394 で接続しておきます。

OS に認識された状態になったら作成したアプリケーションを実行してください。