現在、DirectShowを使ってUSBカメラから画像をダイアログ上のピクチャボックスに表示させるプログラムを作成しています(DirectX8.1bSDK,MFCダイアログベース)。取得した画像をピクチャボックスに表示することはできているのですが、他にフレームレートをダイアログ上に表示させたいのですが方法がわかりません。どのような方法を使えばよいのでしょうか?よろしくお願いします。
編集 削除ある時間内に表示したフレーム数をその時間で割った値がフレームレートです。
例えば10秒で50フレーム表示したらフレームレートは5フレーム/秒です。
レンダリングフィルターからIQualProp インターフェイス
を取得してget_AvgFrameRate()をCALLする。
早速のお返事ありがとうございます。
>レンダリングフィルターからIQualProp インターフェイス
>を取得してget_AvgFrameRate()をCALLする。
最近DirectShowをはじめたばかりでわからないことが多ので、申し訳ありませんがもう少し詳しく教えていただけないでしょうか(フレームレートをメインダイアログ上のエディットボックスに表示させる方法などもできればお願いいたします)。
プログラムはDirectShow8.1bSDKのサンプルのStillCapを使用して作成しています。よろしくお願いします。
HRESULT CStillCap::InitStillGraph()
{
//******************************ここから追加******************************//
HRESULT hr;
// create a filter graph
//
hr = m_pGraph.CoCreateInstance( CLSID_FilterGraph );
if( !m_pGraph )
{
Error( TEXT("Could not create filter graph") );
return E_FAIL;
}
// get whatever capture device exists
//
CComPtr< IBaseFilter > pCap;
GetDefaultCaptureDevice( &pCap );
if( !pCap )
{
Error( TEXT("No video capture device was detected on your system.\r\n\r\n")
TEXT("This sample requires a functional video capture device, such\r\n")
TEXT("as a USB web camera.") );
return E_FAIL;
}
// add the capture filter to the graph
//
hr = m_pGraph->AddFilter( pCap, L"Cap" );
if( FAILED( hr ) )
{
Error( TEXT("Could not put capture device in graph"));
return E_FAIL;
}
// create a sample grabber
//
hr = m_pGrabber.CoCreateInstance( CLSID_SampleGrabber );
if( !m_pGrabber )
{
Error( TEXT("Could not create SampleGrabber (is qedit.dll registered?)"));
return hr;
}
CComQIPtr< IBaseFilter, &IID_IBaseFilter > pGrabBase( m_pGrabber );
// force it to connect to video, 24 bit
//
CMediaType VideoType;
VideoType.SetType( &MEDIATYPE_Video );
VideoType.SetSubtype( &MEDIASUBTYPE_RGB24 );
hr = m_pGrabber->SetMediaType( &VideoType ); // shouldn't fail
if( FAILED( hr ) )
{
Error( TEXT("Could not set media type"));
return hr;
}
// add the grabber to the graph
//
hr = m_pGraph->AddFilter( pGrabBase, L"Grabber" );
if( FAILED( hr ) )
{
Error( TEXT("Could not put sample grabber in graph"));
return hr;
}
// find the two pins and connect them
//
IPin * pCapOut = GetOutPin( pCap, 0 );
IPin * pGrabIn = GetInPin( pGrabBase, 0 );
hr = m_pGraph->Connect( pCapOut, pGrabIn );
if( FAILED( hr ) )
{
Error( TEXT("Could not connect capture pin #0 to grabber.\r\n")
TEXT("Is the capture device being used by another application?"));
return hr;
}
// render the sample grabber output pin, so we get a preview window
//
IPin * pGrabOut = GetOutPin( pGrabBase, 0 );
hr = m_pGraph->Render( pGrabOut );
if( FAILED( hr ) )
{
Error( TEXT("Could not render sample grabber output pin"));
return hr;
}
// ask for the connection media type so we know how big
// it is, so we can write out bitmaps
//
AM_MEDIA_TYPE mt;
hr = m_pGrabber->GetConnectedMediaType( &mt );
if ( FAILED( hr) )
{
Error( TEXT("Could not read the connected media type"));
return hr;
}
VIDEOINFOHEADER * vih = (VIDEOINFOHEADER*) mt.pbFormat;
mCB.m_pCleaningRobotDlg = this;
mCB.m_lWidth = vih->bmiHeader.biWidth;
mCB.m_lHeight = vih->bmiHeader.biHeight;
FreeMediaType( mt );
// don't buffer the samples as they pass through
//
m_pGrabber->SetBufferSamples( FALSE );
// only grab one at a time, stop stream after
// grabbing one sample
//
m_pGrabber->SetOneShot( FALSE );
// set the callback, so we can grab the one sample
//
m_pGrabber->SetCallback( &mCB, 1 );
// find the video window and stuff it in our window
//
CComQIPtr< IVideoWindow, &IID_IVideoWindow > pWindow = m_pGraph;
if( !pWindow )
{
Error( TEXT("Could not get video window interface"));
return E_FAIL;
}
// set up the preview window to be in our dialog
// instead of floating popup
//
HWND hwndPreview = NULL;
GetDlgItem( IDC_LIVE_VIDEO_PREVIEW_PICT, &hwndPreview );
RECT rc;
::GetWindowRect( hwndPreview, &rc );
pWindow->put_Owner( (OAHWND) hwndPreview );
pWindow->put_Left( 0 );
pWindow->put_Top( 0 );
pWindow->put_Width( rc.right - rc.left );
pWindow->put_Height( rc.bottom - rc.top );
pWindow->put_Visible( OATRUE );
pWindow->put_WindowStyle( WS_CHILD | WS_CLIPSIBLINGS );
// Add our graph to the running object table, which will allow
// the GraphEdit application to "spy" on our graph
#ifdef REGISTER_FILTERGRAPH
hr = AddGraphToRot(m_pGraph, &g_dwGraphRegister);
if (FAILED(hr))
{
Error(TEXT("Failed to register filter graph with ROT!"));
g_dwGraphRegister = 0;
}
#endif
// run the graph
//
CComQIPtr< IMediaControl, &IID_IMediaControl > pControl = m_pGraph;
hr = pControl->Run( );
if( FAILED( hr ) )
{
Error( TEXT("Could not run graph"));
return hr;
}
UpdateStatus(_T("ライブビデオのプレビュー"));
return 0;
//******************************ここまで追加******************************//
}
IQualProp *ptr;
pWindow->QueryInterface(IID_IQualProp,(void**)&ptr);
で、IQualPropインターフェースを取得できませんか?
> フレームレートをメインダイアログ上のエディットボックスに
> 表示させる方法
エディットボックスの貼り付け方・使い方はわかっていますか?
お返事ありがとうございます。
以下のコードをUpdateStatusの直前に記述してみたのですが、実行するとエラーが出てしまいます。IQualPropインターフェイスについて調べてみたのですが、参考になるホームページがほとんど見当たらなかったので解決することができません。間違っている部分はどのあたりでしょうか?よろしくお願いします。
<追加したコード>
CComQIPtr< IQualProp, &IID_IQualProp > ptr = m_pGraph;
hr = ptr->get_AvgFrameRate(&framerate);(framerate:int型のグローバル変数として宣言)
if( FAILED( hr ) )
{
Error( TEXT("Could not get frame rate"));
return hr;
}
CComQIPtr< IQualProp, &IID_IQualProp > ptr (pWindow);
じゃあないですか。フィルタグラフはIQualPropインターフェースを
知りません。
ptrがNULLかどうかのチェックをかけた方がいいです。
「SampVid フィルタ サンプル」でIQualPropインターフェース
を使っています。
次の2つの方法でやってみたのですが、途中でエラーが発生します。
(方法1)
CComQIPtr< IQualProp, &IID_IQualProp > ptr(pWindow);
hr = ptr->get_AvgFrameRate(&framerate);<-ここでエラー発生
if( FAILED( hr ) )
{
Error( TEXT("Could not get frame rate"));
return hr;
}
(方法2)
IQualProp *ptr = NULL;
hr = pWindow->QueryInterface(IID_IQualProp,(void**)&ptr);<-ここでエラー発生
if( FAILED( hr ) )
{
Error( TEXT(""));
return hr;
}
hr = ptr->get_AvgFrameRate(&framerate);
if( FAILED( hr ) )
{
Error( TEXT("Could not get frame rate"));
return hr;
}
あと、コードを記述する位置などは間違ってはいないでしょうか?
あ、ごめん。どうやらIVideoWindowはIQualPropを知らないみたい。
こうなったら、レンダラーを取り出してこれに聞いてみるしか
ないです。
IQualProp *ptr = NULL;
IBaseFilter *video = NULL;
m_pGraph->FindFilterByName(L"Video Renderer", &video);
video->QueryInterface(IID_IQualProp,(void**)&ptr);
video->Release();
エラーチェックはきちんとやってね。
FindFilterByNameでレンダラーがつかまらない
場合は、IFilterGraph::EnumFilters()で芋づる式に取出すしかない。
教えていただいたとおりにやってみたのですが、ビデオレンダラを取り出すことができませんでした。「StillCap」というサンプルを参考にしてこのプログラムを作成しているのですが、ビデオレンダラを作成しているのはどの部分になるのでしょうか(キャプチャフィルタとサンプルグラバフィルタはわかるのですが・・・)?
あと、コールバック関数を使用しているので、フレームレートを取得する他の手段として、コールバック関数内で関数が呼ばれた回数を変数に入れ、それをOnTimer関数で一秒ごとにエディットボックスに表示させるという方法はどうなのでしょうか?
> ビデオレンダラを作成しているのはどの部分になるのでしょうか
hr = m_pGraph->Render( pGrabOut );
でデフォルトのレンダラーを自動的にくっつけています。
私で試したところ(OSはXP)、このコードの後では
レンダラとIQualPropをきちんと取り出せます。
> コールバック関数内で関数が呼ばれた回数を変数に入れ、
> それをOnTimer関数で一秒ごとにエディットボックスに
> 表示させるという方法はどうなのでしょうか?
レンダラーに到達したフレーム数と実際に描画できたフレーム数
が違う可能性もあります。
UpdateStatusの直前に次のコードを記述しました。
CComPtr< IBaseFilter > pVideoRender;
hr = m_pGraph->FindFilterByName(L"Video Renderer",&pVideoRender);
if( FAILED( hr ) )
{
Error( TEXT("Could not find VideoRender"));
return hr;
}
CComQIPtr< IQualProp, &IID_IQualProp > ptr(pVideoRender);
hr = ptr->get_AvgFrameRate(&framerate);
if( FAILED( hr ) )
{
Error( TEXT("Could not Frame Rate"));
return hr;
}
デバッグで調べた結果、
hr = m_pGraph->FindFilterByName(L"Video Renderer",&pVideoRender)
で、関数の返り値が0なのでレンダラは取り出せていると思います。しかし、
hr = ptr->get_AvgFrameRate(&framerate);
で、framerate = 0となっています。フレームレートが取得できていないということでしょうか?
> framerate = 0となっています
Run()直後なら、フレームがまだ流れてないので0になると思うが。
CComQIPtr< IMediaEvent, &IID_IMediaEvent > event = m_pGraph;
long code;
event->WaitForCompletion(3000, &code);
をRun()後にいれて3秒間待つとどうなる?
OnTimer()の使い方はわかるよね?
CComQIPtr< IMediaControl, &IID_IMediaControl > pControl = m_pGraph;
hr = pControl->Run( );
if( FAILED( hr ) )
{
Error( TEXT("Could not run graph"));
return hr;
}
hr = m_pGraph->FindFilterByName(L"Video Renderer",&m_pVideoRender);
if( FAILED( hr ) )
{
Error( TEXT("Could not find VideoRender"));
return hr;
}
として(m_pVideoRenderはメンバ変数として宣言しています),OnTimerを
void CStillCapDlg::OnTimer(UINT nIDEvent)
{
// TODO: この位置にメッセージ ハンドラ用のコードを追加するかまたはデフォルトの処理を呼び出してください
if(nIDEvent == 1) { //1秒ごと
CComQIPtr< IQualProp, &IID_IQualProp > ptr(m_pVideoRender);
ptr->get_AvgFrameRate(&framerate);
FrameRateStatic.Format("%d",framerate);
SetDlgItemText(IDC_FRAME_RATE_STATIC, FrameRateStatic);
}
CDialog::OnTimer(nIDEvent);
}
にすることで、framerateの値をスタティックテキストに表示することができました(OnTimerでの記述がこれでよいのかわかりませんが・・・)が、framerateの値が約2000ぐらいになっています。取得したフレームレートの桁はどうなっているのでしょうか?
> OnTimerでの記述がこれでよいのかわかりませんが
こんなもんじゃないすっか。
> framerateの値が約2000ぐらいになっています
DirectX日本語ヘルプのIQualProp::get_AvgFrameRate()の項に
「1 秒あたりの実際のフレーム数に 100 を掛けた数を受け
取る変数へのポインタ」
とあります。
無事解決しました。ありがとうございました。
編集 削除チェック忘れてました。
編集 削除