本文档比较适合初学者,写的比较详细,水平有限,翻译得不好还请见谅。
目标是只要不会误导大家就可以了,我可不想误人子弟,哈哈!
(注)ドキュメントの Help(...)は,DirectX 8.0 日本語へルプ をデフォルトのパス(C:/mssdk/doc/DX8JHelp/directx8_c.chm)にインストールしたものとして,ローカルディスクにリンクを張っている.
まぁ,要はインタフェースが重要らしい
dll入れ替えられるんやね
CoCreateInstanceでインスタンスつくって,インタフェースのポインタは QueryInterfaceで取得する
GUID(CLSID, IID)でインタフェースや実装クラスを識別する
こんなんで,ええか?
CComPtr ? ATL ? はあとまわし
DirectXのへルプにあるDirectShowのファーストステップを参考に,まずは DirectShow のHellow World と呼ばれる サンプルコードを書いてみる.
#include
#include
using namespace std;
void main()
{
cout << "Aiko 花火: [Enter]" << endl;
getchar();
CoInitialize(NULL);
IGraphBuilder *pGraph;
IMediaControl *pMediaControl;
IMediaEvent *pEvent;
CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER,
IID_IGraphBuilder, (void **)&pGraph);
pGraph->QueryInterface(IID_IMediaControl, (void **)&pMediaControl);
pGraph->QueryInterface(IID_IMediaEvent, (void **)&pEvent);
// グラフを作成
pGraph->RenderFile(L"D://aiko-hanabi.mpg", NULL);
// グラフの実行
pMediaControl->Run();
// 終了を待つ
long evCode;
pEvent->WaitForCompletion(INFINITE, &evCode);
// クリーンアップ
pMediaControl->Release();
pEvent->Release();
pGraph->Release();
CoUninitialize();
return 0;
}
あと,IVideoWindow や IMediaEventEx のサンプルもあるので試す. (WinMain で書かれているが,Dialog with MFC ぐらいにしてみる)
フィルタグラフ・・・フィルタとピン
ストリーム・・・
行く河の流れは絶えずしてしかももとの水にはあらず
うう,よくわからんが,
流れ(stream)を制(control)する者はDirectShowを制する
(グラフ内の)streamが全て絶えたときは EC_COMPLETE イベントがアプリケーションに送られるらしい
まずは,Help(DirectShow の使い方 → ビデオキャプチャ ,DirectShowチュートリアル → AVIファイルの再圧縮 ) を参考にビデオキャプチャの流れをまとめてみよう.
(最初と最後にそれぞれ CoInitialize(NULL), CoUninitialize()を呼ぶのを忘れないように・・・)
1. | フィルタグラフ作成 | CoCreateInstance( CLSID_FilterGraph ) |
2. | キャプチャデバイスの取得 | ICreateDevNum, IEnumMoniker, IMoniker, IBaseFilter |
3. | キャプチャビルダグラフ | ICaptureGraphBuilder2 |
3-1. キャプチャビルダグラフ作成 | CoCreateInstance( CLSID_CaptureGraphBuilder2 ) | |
3-2. フィルタグラフへの関連付け | ICaptureGraphBuilder2::SetFilterGraph((IFilterGraph)pGraph) | |
4. | ファイルライタフィルタ | IFileSinkFilter |
4-1. フィルタグラフへの追加 | ICaptureGraphBuilder2::SetOutputFileName(...) | |
4-2. ストリームのレンダリング設定 | ICaptureGraphBuilder2::RenderStream(...) | |
5. | キャプチャ開始 | IMediaControl::Run() |
2のデバイス列挙子の使用に付いては以下 にもう少し詳しくまとめる.
Help ( DirectShowチュートリアル → デバイスとフィルタの列挙 → システムデバイスの列挙子の使用 ) 参照
2-1. | デバイスの列挙子を取得 | ICreateDevEnum | CoCreateInstanceによってインスタンス生成 |
2-2. | カテゴリの列挙子を取得 | IEnumMoniker | ICreateDevEnum::CreateClassEnumerator(カテゴリ名, ...) によって入手 |
2-3. | モニカを取得 | IMoniker | IEnumMoniker::Next によって列挙 |
2-4. | フィルタにバインド | IBaseFilter | IMoniker::BindToObject(..., (void **)pSrc) |
フレンドリ名を取得するには, 4で IMoniker::BindToStrage を用いてプロパティバッグを取得し,IPropertyBag::Read を用いるらしい.
(注)簡略化のためエラー処理一切なし.
#include
#include
using namespace std;
int main(int argc, char* argv[])
{
CoInitialize(NULL);
// 1. フィルタグラフ作成
IGraphBuilder *pGraph = NULL;
CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC,
IID_IGraphBuilder, (void **)&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();
pGraph->AddFilter(pSrc, L"Video Capture");
// 3. キャプチャビルダの作成
ICaptureGraphBuilder2 *pBuilder = NULL;
CoCreateInstance(CLSID_CaptureGraphBuilder2, NULL, CLSCTX_INPROC,
IID_ICaptureGraphBuilder2, (void **)&pBuilder);
pBuilder->SetFiltergraph(pGraph);
// 4. ファイルライタフィルタの設定
IBaseFilter *pMux = NULL;
IFileSinkFilter *pSink = NULL;
// ファイルへ
pBuilder->SetOutputFileName(&MEDIASUBTYPE_Avi, L"C://dvcap_tmp.avi", &pMux, &pSink);
pBuilder->RenderStream(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Video, pSrc, NULL, pMux);
REFERENCE_TIME rtStart = 50000000, rtStop = 80000000;
pBuilder->ControlStream( &PIN_CATEGORY_CAPTURE, &MEDIATYPE_Video, pSrc, &rtStart, &rtStop,
0, 0 );
// ディスプレイへ
pBuilder->RenderStream( &PIN_CATEGORY_PREVIEW, &MEDIATYPE_Video, pSrc, NULL, NULL );
// 5. キャプチャ開始
IMediaControl *pMediaControl;
pGraph->QueryInterface(IID_IMediaControl, (void **)&pMediaControl);
pMediaControl->Run();
MessageBox(NULL, "Click to stop.", "DirectShow", MB_OK);
// 6. 終了
pSrc->Release();
pMux->Release();
pSink->Release();
pMediaControl->Release();
pEvent->Release();
pBuilder->Release();
pGraph->Release();
CoUninitialize();
cout << "end/n";
return 0;
}
(注)Preview を見ていると,キャプチャされた画像の色数がやたらと少ない場合(おそらく16bpp)がある.これは, YUVから RGBへの変換方法に依存するようだ.この辺は,キャプチャドライバとグラフィックカードの組合わせによって異なる.
一般には,AVIデコンプレッサというのがこの変換を司るが,グラフィックカードがハードウェア的にサポートしていれば(かつ VideoRenderer で YUV Overlays/Flipping/OffScreen等 がオンになっていれば),AVIデコンプレッサはYUVを素通りさせ,直接ビデオメモリのDirectDrawオーバレイサーフェイスにコピーする.グラ フィックカードがサポートしていない場合は,AVIデコンプレッサが MSYUV CODEC を 用いてソフト的に変換を行う.しかし,このMSYUVはRGB16, RGB8への変換しかできないらしく,この場合に色数が少なくなる.RGB24,32をサポートするCODECを用いれば良いのだが・・・(例え ば,Unibrain Fire-i のキャプチャドライバでは,MSYUV CODECでなく,独自のYUV CODEC (fiyuv.dll, fiyuv.ax(DirectShow Filter)) によってRGB32を実現している)(<--もはや昔の話です.今のDirectshow9のフィルタはRGB32になります.)
まあ,あくまで表示(VideoRenderer)の話なので,ファイルに落とすとYUVのまま保存されているようだ.
参照 Help( DirectShowリファレンス → フィルタ → MSYUVカラースペース変換 CODEC )
Help ( DirectShowチュートリアル → フィルタグラフ内のオブジェクトの列挙 → フィルタの列挙 ) 参照
1. | フィルタ列挙子の取得 | IEnumFilters | IFilterGraph::EnumFilters |
2. | フィルタの列挙 | IBaseFilter | IEnumFilters::Next(1, (IBaseFilter **), (ULONG *)) |
3. | フィルタ名の取得 | FILTER_INFO | IBaseFilter::QueryFilterInfo(FILTER_INFO *) |
Help ( DirectShowチュートリアル → フィルタのプロパティページの表示 ) を参考に・・・
直接生成したフィルタ以外は,フィルタへのポインタを持っていない.そこで, フィルタの列挙子 を用いて,順にグラフ内のフィルタ(へのポインタ)を取得する.もしくは,フィルタ名が分かっているときは IFilterGraph::FindFilterByName メソッドを用いる.
4. | プロパティページインタフェース取得 | ISpecifyPropertyPages | IBaseFilter::QueryInterfase(IID_ISpecifyPropertyPages, (void **)) |
5. | プロパティページの取得 | CAUUID | ISpecifyPropertyPages::GetPages(CAUUID *) ISpecifyPropertyPages::Release() |
6. | プロパティページの表示 | OleCreatePropertyFrame | |
7. | GUID配列の解放 | CoTaskMemFree(CAUUID::pElems) |
CAUUID は GUIDのカウント配列が定義される構造体.初期化は要らない.各プロパティページのクラス識別(CLSID)がCAUUID::pElemsに入る. OleCreatePropertyFrameによって作成されるダイアログはモーダル.
5. キャプチャ開始 の直前にプロパティページを表示させてみてもいいかも.
// フィルタを列挙し、それらのプロパティ ページを表示する。
IEnumFilters *pEnum;
IBaseFilter *pFilter = NULL;
HRESULT hr;
pGraph->EnumFilters(&pEnum);
while(pEnum->Next(1, &pFilter, NULL) == S_OK)
{
ISpecifyPropertyPages *pSpecify;
hr = pFilter->QueryInterface(IID_ISpecifyPropertyPages, (void **)&pSpecify);
if (SUCCEEDED(hr)) {
FILTER_INFO FilterInfo;
pFilter->QueryFilterInfo(&FilterInfo);
CAUUID caGUID;
pSpecify->GetPages(&caGUID);
pSpecify->Release();
OleCreatePropertyFrame(
NULL, // 親ウィンドウ
0, // x (予約済み)
0, // y (予約済み)
FilterInfo.achName, // ダイアログ ボックスのキャプション
1, // フィルタの数
(IUnknown **)&pFilter, // フィルタへのポインタ
caGUID.cElems, // プロパティ ページの数
caGUID.pElems, // プロパティ ページ CLSID へのポインタ
0, // ロケール識別子
0, // 予約済み
NULL // 予約済み
);
CoTaskMemFree(caGUID.pElems);
FilterInfo.pGraph->Release();
}
pFilter->Release();
}
pEnum->Release();
どうも,ビデオキャプチャーだと動的に決まるパラメータが多いので,キャプチャー前にモーダルダイアログでプロパティを表示させても意味無いな.後でモードレスなプロパティボックスにしよう.
流れのなかに網を仕掛けておくイメージで,上のビデオキャプチャのstream中にサンプルグラバを挿入する(ちょっと違うか.).これによって, フィルタを通過していくサンプルを獲得できるそうだ.これは SampleGrabberフィルタを作成し, ISampleGrabberインタフェースを用いて行うが,次の2つの方法があるらしい.
ここでは,まずは前者を試してみることにしよう.(ちなみにDirectShowサンプルStillCap は後者を用いている)
Help ( DirectShow チュートリアル → メディアサンプルの獲得 ) 参照.
基本的に,1から3はビデオキャプチャ と同じだが,4を次のようにする.
4-1. サンプルグラバの生成 | IBaseFilter ISampleGrabber |
CoCreateInstance(CLSID_SampleGrabber, ..., IID_IBaseFilter, ...) IBaseFilter::QueryInterfase(IID_ISampleGrabber, ...) |
4-2. メディアタイプの設定 | AM_MEDIA_TYPE | ISampleGrabber::SetMediaType(AM_MEDIA_TYPE *) |
4-3. フィルタグラフへ追加 | IFilterGraph::AddFilter(IBaseFilter *) | |
4-4. サンプルグラバの接続 | 様々な方法があるらしい | |
4-5. グラバのモードを適切に設定 | ISampleGrabber::SetBufferSample(TRUE) : 毎サンプルをバッファにコピーするかどうか ISampleGrabber::SetOneShot(FALSE) : 1サンプルでグラフを終了するか |
|
5. キャプチャ中にグラブを行う | ISampleGrabber::GetCurrentBuffer(...) |
サンプルグラバの接続は・・・次のようなやり方があるらしいが.
1と3は,ある程度自動で構築するみたいやけど,今はどこに接続するかを理解するのが大切なので,ここでは,2のピンで指定してサンプルグラバフィルタを挿入するやり方をとる.フィルタを追加した際のピンの接続に関しては,Help ( DirectShow について → フィルタグラフの構築 → 一般的なグラフの構築 ) 参照.
(注) ISampleGrabberを用いるには, #include
まず,指定した方向のピンを得るための関数をつくる. Help ( DirectShowチュートリアル → フィルタグラフ内のオブジェクトの列挙 → ピンの列挙 ) 参照
1. | ピン列挙子の取得 | IEnumPins | IBaseFilter::EnumPins |
2. | ピンの列挙 | IPin | IEnumPins::Next(1, (IPin **), 0) |
3. | ピンの方向(IN,OUT)の取得 | FILTER_INFO | IPin::QueryDirection(PIN_DIRECTION *) |
IPin *GetPin(IBaseFilter *pFilter, PIN_DIRECTION PinDir)
{
BOOL bFound = FALSE;
IEnumPins *pEnum;
IPin *pPin;
pFilter->EnumPins(&pEnum);
while(pEnum->Next(1, &pPin, 0) == S_OK)
{
PIN_DIRECTION PinDirThis;
pPin->QueryDirection(&PinDirThis);
if (bFound = (PinDir == PinDirThis)) // 引数で指定した方向のピンならbreak
break;
pPin->Release();
}
pEnum->Release();
return (bFound ? pPin : 0);
}
ただし,ISampleGrabber を用いるために #include
// 4. 一枚撮る
// 4-1. サンプルグラバの生成
IBaseFilter *pF = NULL;
ISampleGrabber *pSGrab;
CoCreateInstance(CLSID_SampleGrabber, NULL, CLSCTX_INPROC_SERVER,
IID_IBaseFilter, (LPVOID *)&pF);
pF->QueryInterface(IID_ISampleGrabber, (void **)&pSGrab);
// 4-2. メディアタイプの設定
AM_MEDIA_TYPE mt;
ZeroMemory(&mt, sizeof(AM_MEDIA_TYPE));
mt.majortype = MEDIATYPE_Video; // Sample Grabber の入力ピン(Capture Device の出力ピン)はUYVY
mt.subtype = MEDIASUBTYPE_UYVY;
mt.formattype = FORMAT_VideoInfo;
pSGrab->SetMediaType(&mt);
// 4-3. フィルタグラフへ追加
pGraph->AddFilter(pF, L"Grabber");
// 4-4. サンプルグラバの接続
// [pSrc](o) -> (i)[pF](o) -> [VideoRender]
// ↑A ↑B ↑C
IPin *pSrcOut = GetPin(pSrc, PINDIR_OUTPUT); // A
IPin *pSGrabIN = GetPin(pF, PINDIR_INPUT); // B
IPin *pSGrabOut = GetPin(pF, PINDIR_OUTPUT); // C
pGraph->Connect(pSrcOut, pSGrabIN);
pGraph->Render(pSGrabOut);
// 4-5. グラバのモードを適切に設定
pSGrab->SetBufferSamples(TRUE); // GetCurrentBuffer を用いる際には必ずTRUE (FALSE では失敗する)
pSGrab->SetOneShot(FALSE); // ワンショットをOFFにする
// 5. キャプチャ開始
IMediaControl *pMediaControl = NULL;
pGraph->QueryInterface(IID_IMediaControl, (void **)&pMediaControl);
pMediaControl->Run();
long *buffer = NULL;
long bufsize = 0;
// サイズ取得
do{
pSGrab->GetCurrentBuffer(&bufsize, NULL);
}while(bufsize <= 0);
cerr << "bufsize : " << bufsize << endl;
buffer = (long *)malloc(sizeof(char)*bufsize);
if(buffer==NULL){
cerr << "can't allocate memory/n";
return 1;
}
// 一枚キャプチャ
MessageBox(NULL, "Click to capture & stop.", "DirectShow", MB_OK);
// バッファ取得
hr = pSGrab->GetCurrentBuffer(&bufsize, buffer);
if(hr==S_OK){
cerr << "Capture Succeeded/n";
}
// 6. 終了
pF->Release(); // こいつらも
pSGrab->Release(); // わすれずに解放
pSrc->Release();
pMediaControl->Release();
pBuilder->Release();
pGraph->Release();
CoUninitialize();
メディアタイプはHelp( DirectShow リファレンス → 定数とGUID → メディアタイプ )参照.
基本的に先のスチルキャプチャと同様であるが,受け取るサンプル毎にコールバックを呼び出すことができるので,これを利用してバッファの内容をファ イルに落とすことにする.コールバック関数としては SampleCBかBufferCBを実装せなあかんらしいが,両者の引数を見てみると,その違いはこんな感じやと思われる.
ここでは,より単純やと思われる後者を実装する.
0. ISampleGrabberCBインタフェースの実装 | ISampleGrabberCB | メソッドとしてSampleCBかBufferCBのいずれか一つを実装する |
4-5. グラバのモードを適切に設定 | SetBufferSample(FALSE), SetOneShot(FALSE) SetCallback() : コールバックメソッドを指定 第1引数:ISampleGrabberCB実装クラスのインスタンスへのポインタ 第2引数:どちらのメソッドを用いるか.0ならSampleCB, 1ならBufferCB |
Help( DirectShowチュートリアル → メディアサンプルの獲得 → コールバックメソッドの利用 )参照
(注)streams.hをインクルードし,strmbase.lib(Release)もしくは strmbasd.lib(Debug)をリンクする必要がある.
ISampleGrabberCBインタフェースを継承し,メソッドを実装する.ただし,これはCOMに基づいて実装する必要があるため,簡略化の ためCUnknown も継承する.これによって,IUnknown のAddRef, Release, QueryInterfaceといったメソッドを1から実装する手間が省ける.その代わり,NonDelegatingQueryInterfaceを実 装する.(サンプル StillCap では,CUnknownは継承せず1から実装している)
class CGrabCB: public CUnknown, public ISampleGrabberCB
{
public:
DECLARE_IUNKNOWN;
STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void **ppv)
{
if( riid == IID_ISampleGrabberCB ){
return GetInterface((ISampleGrabberCB*)this, ppv);
}
return CUnknown::NonDelegatingQueryInterface(riid, ppv);
}
// ISampleGrabberCB のメソッド
STDMETHODIMP SampleCB(double SampleTime, IMediaSample *pSample)
{
return E_NOTIMPL;
}
STDMETHODIMP BufferCB(double SampleTime, BYTE *pBuffer, long BufferLen)
{
cerr << "Sample time: " << SampleTime << "/t";
cerr << "BufferLen: " << BufferLen;
cerr << endl;
SaveOneImage("C://tmp.yuv", pBuffer, BufferLen); // 後に定義する
return S_OK;
}
// コンストラクタ
CGrabCB( ) : CUnknown("SGCB", NULL)
{ }
};
インプリメントしないメソッド(この場合はSampleCB)では E_NOTIMPL を返すようにする.
まずSaveOneImageを定義しておく.これはsaveflagがTRUEになる度に,一回だけバッファ内容をファイルに書き出す関数である.
BOOL saveflag=FALSE;
inline BOOL SaveOneImage(const char *fname, BYTE *pBuffer, long BufferLen)
{
if(saveflag){
ofstream fout(fname, ios::out | ios::binary);
if(!fout.is_open()) return FALSE;
fout.write((char *)pBuffer, BufferLen);
fout.close();
saveflag=FALSE;
}
return TRUE;
}
次に,stillcap_cosole1のmain関数内で変更する部分を示す.
// 4-5. グラバのモードを適切に設定
pSGrab->SetBufferSamples(FALSE);
pSGrab->SetOneShot(FALSE);
CGrabCB *cb = new CGrabCB();
pSGrab->SetCallback(cb, 1); // 第2引数でコールバックを指定 (0:SampleCB, 1:BufferCB)
// 5. キャプチャ開始
IMediaControl *pMediaControl = NULL;
IMediaEvent *pEvent = NULL;
pGraph->QueryInterface(IID_IMediaControl, (void **)&pMediaControl);
pGraph->QueryInterface(IID_IMediaEvent, (void **)&pEvent);
long evCode;
pMediaControl->Run();
MessageBox(NULL, "Click to capture & stop.", "DirectShow", MB_OK);
saveflag = TRUE;
pEvent->WaitForCompletion(100, &evCode);
CGrabCB のインスタンスを作成し,SetCallbackで設定しているぐらいやね. MessageBoxにOKしたあとにsaveflagをTRUEにする.すると,SaveOneImageはそのときのバッファの内容をファイルを保存 する.コールバックはスレッドで呼ばれるため,WaitForCompletionを用いて保存する時間を確保している(ばかちょんでやけど).
ビデオデバイスのドライバが, WDM (Windows Deriver Model) に基づいて書かれたビデオキャプチャフィルタであるとする.これが IAMCameraControlをサポートしていれば,次のような手順でzoom,focus・・・などのパラメタをコントロールできるはず.同様 に,IAMVideoProcAmpを用いて,輝度,コントラスト,色相,彩度,ガンマ,鮮明度などを調節できる.
a. デバイスのインタフェイス取得 | IAMCameraControl, IAMVideoProcAmp | IBaseFilter::QueryInterface(...) |
b. パラメタの範囲取得 | GetRange(...) |
|
c. パラメタコントロール | Set(...) |
|
b. パラメタ取得 | Get(...) |
これはほとんどvideocapture_console そのまま.ただし,ファイル出力部分を削除した.
.....
#define NSTEP 5
.....
// 3.
ICaptureGraphBuilder2 *pBuilder = NULL;
CoCreateInstance(CLSID_CaptureGraphBuilder2, NULL, CLSCTX_INPROC,
IID_ICaptureGraphBuilder2, (void **)&pBuilder);
pBuilder->SetFiltergraph(pGraph);
pBuilder->RenderStream( &PIN_CATEGORY_PREVIEW, &MEDIATYPE_Video, pSrc, NULL, NULL );
// カメラコントロール
IAMCameraControl *pCameraControl;
CameraControlProperty prop;
pSrc->QueryInterface(IID_IAMCameraControl, (void **)&pCameraControl);
long Min, Max, SteppingDelta, Default, CapsFlags;
prop = CameraControl_Zoom;
pCameraControl->GetRange(prop, &Min, &Max, &SteppingDelta, &Default, &CapsFlags);
cerr << "Min : " << Min << endl;
cerr << "Max : " << Max << endl;
cerr << "Stepping Delta : " << SteppingDelta << endl;
cerr << "Default : " << Default << endl;
cerr << "CapsFlags : " << CapsFlags << endl; long zoom;
long cflags;
pCameraControl->Get(prop, &zoom, &cflags);
cerr << "zoom : " << zoom << endl;
// 5. キャプチャ
IMediaControl *pMediaControl;
pGraph->QueryInterface(IID_IMediaControl, (void **)&pMediaControl);
pMediaControl->Run();
long setzoom=Min;
long step = (Max-Min)/NSTEP;
cerr << "step : " << step << endl;
for(int i=0; i < NSTEP; i++){
if(i==NSTEP-1) setzoom = Min; // zoom:Minで終える
MessageBox(NULL, "Click to zoom up", "DirectShow", MB_OK);
pCameraControl->Set(prop, setzoom, cflags);
int diff;
do{
pCameraControl->Get(prop, &zoom, &cflags);
cerr << "zoom : " << zoom << endl;
diff = zoom - setzoom;
cerr << "diff*diff: " << diff*diff << endl;
}while(diff*diff > 1);
setzoom += step;
}
MessageBox(NULL, "Click to stop.", "DirectShow", MB_OK);
// 6.
pCameraControl->Release();
pSrc->Release();
.....
便利やし,ヘルプそのままやけどメモっとこ.自分の書いたアプリケーションで,フィルタグラフがどのような構成になっているかを,GraphEditを用いて目で確認できる.GraphEditからプリントしてpsファイル等に落とせば,ドキュメントにもしやすいし.
HRESULT AddToRot(IUnknown *pUnkGraph, DWORD *pdwRegister)
{
IMoniker * pMoniker;
IRunningObjectTable *pROT;
if (FAILED(GetRunningObjectTable(0, &pROT))) {
return E_FAIL;
}
WCHAR wsz[256];
wsprintfW(wsz, L"FilterGraph %08p pid %08x", (DWORD_PTR)pUnkGraph, GetCurrentProcessId());
HRESULT hr = CreateItemMoniker(L"!", wsz, &pMoniker);
if (SUCCEEDED(hr)) {
hr = pROT->Register(0, pUnkGraph, pMoniker, pdwRegister);
pMoniker->Release();
}
pROT->Release();
return hr;
}
void RemoveFromRot(DWORD pdwRegister)
{
IRunningObjectTable *pROT;
if (SUCCEEDED(GetRunningObjectTable(0, &pROT))) {
pROT->Revoke(pdwRegister);
pROT->Release();
}
}
・・・
// 1. フィルタグラフ作成
IGraphBuilder *pGraph = NULL;
CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC, IID_IGraphBuilder, (void **)&pGraph);
// FilterGraph作成後にRot(実行時オブジェクトテーブル)に追加
DWORD dwRegister;
hr = AddToRot(pGraph, &dwRegister);
・・・
// Graphのリリース前にRotから削除
RemoveFromRot(dwRegister);
pGraph->Release();
・・・
ちなみに videocap_consoleはこんなグラフになった.
これも一応メモっとこっと.
毎回毎回 「Press any key to continue」に答えるのは面倒.もしコンソールが必要ないなら,[設定] → [リンク] → [プロジェクトオプション] の最後に
/SUBSYSTEM:WINDOWS /ENTRY:mainCRTStartup
を加える.
カメラが複数になっても,一つのグラフに追加できるみたいやな.とりあえずやってみよう.ただし,今はカメラ間の同期などは考えないものとする.
・・・
#define NCAM 2
int main(int argc, char* argv[])
{
HRESULT hr;
CoInitialize(NULL);
// 1. フィルタグラフ作成
IGraphBuilder *pGraph = NULL;
CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC,
IID_IGraphBuilder, (void **)&pGraph);
DWORD dwRegister;
hr = AddToRot(pGraph, &dwRegister);
// 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[NCAM];
int i;
for(i=0; i < NCAM; i++){
if (pClassEnum->Next(1, &pMoniker, &cFetched) == S_OK){
// NCAM番目のモニカまでフィルタオブジェクトにバインドする
pMoniker->BindToObject(0, 0, IID_IBaseFilter, (void **)&pSrc[i]);
pMoniker->Release();
}
pGraph->AddFilter(pSrc[i], L"Video Capture");
}
pClassEnum->Release();
pDevEnum->Release();
// 3. キャプチャビルダの作成
ICaptureGraphBuilder2 *pBuilder[NCAM];
for(i=0; i < NCAM; i++){
CoCreateInstance(CLSID_CaptureGraphBuilder2, NULL, CLSCTX_INPROC,
IID_ICaptureGraphBuilder2, (void **)&pBuilder[i]);
pBuilder[i]->SetFiltergraph(pGraph);
// ディスプレイへ
pBuilder[i]->RenderStream( &PIN_CATEGORY_PREVIEW, &MEDIATYPE_Video, pSrc[i], NULL, NULL );
}
// 5. キャプチャ開始
IMediaControl *pMediaControl;
pGraph->QueryInterface(IID_IMediaControl, (void **)&pMediaControl);
pMediaControl->Run();
MessageBox(NULL, "Click to stop.", "DirectShow", MB_OK);
// 6. 終了
for(i=0; i < NCAM; i++){
pSrc[i]->Release();
pBuilder[i]->Release();
}
pMediaControl->Release();
RemoveFromRot(dwRegister);
pGraph->Release();
CoUninitialize();
return 0;
}
GraphEditで表示させると図のようになった.いけとるみたいやん.というわけで終了.