// cap10.cpp DirectShowを使ったキャプチャの応用 ver10 // 最も簡単な例 // ○コンソールアプリケーションで作成すること // ○[プロジェクト][設定][C/C++] //  [コード生成][ランタイム]にマルチスレッドを設定すること // ○[プロジェクト][設定][リンク] //  [一般][オブジェクト/ライブラリモジュール]に //  c:\mssdk\lib\strmiids.lib を追加 8.0sdk //   (c:\mssdkにSDKをインストールした場合) // c:\dxsdk\lib\strmiids.lib を追加 8.1sdkの場合 // ○COMの操作はすべてHRESULTを返すが、プログラムを簡単にするため // 最低限の吟味しか行っていない。 // MOTION TEST // 2008.03.01 masato at bird.email.ne.jp #include #include #include #include #include // SampleGrabber用 #include #define MTEST 1 // test // グラフィック用ウィンドウのデータをまとめた構造体 typedef struct{ HINSTANCE hi; HWND hwnd; // 自分のウィンドウハンドル BYTE *lpBmpData; // BMPのデータ部分 BITMAPINFOHEADER bih; } IMG0; DWORD th_Proc( void ); LRESULT CALLBACK grProc( HWND, UINT, WPARAM, LPARAM ); IMG0 img00; #if MTEST // MOTION TEST #define DELAY 20 BYTE **bmps; // frame buffer int frames; // n of frames long nn = 0; // frame counter int sw = 1; #endif main() { IGraphBuilder * pGraph = NULL; IMediaControl * pMC = NULL; ICaptureGraphBuilder2 * pCapture = NULL; IBaseFilter *pF = NULL; ISampleGrabber *pGrab = NULL; // これらは後で解放すること。 HRESULT hr; AM_MEDIA_TYPE amt; CoInitialize(NULL); // COMの初期化 // ---- キャプチャフィルタの準備 ---- // キャプチャデバイスを探す IBaseFilter *pbf = NULL; IMoniker * pMoniker = NULL; ULONG cFetched; // デバイス列挙子を作成 ICreateDevEnum * pDevEnum =NULL; CoCreateInstance( CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC, IID_ICreateDevEnum, (void ** ) &pDevEnum); // ビデオキャプチャデバイス列挙子を作成 IEnumMoniker * pClassEnum = NULL; pDevEnum -> CreateClassEnumerator( CLSID_VideoInputDeviceCategory, &pClassEnum, 0); if (pClassEnum == NULL){ printf("ビデオキャプチャデバイスは存在しません\n"); pDevEnum -> Release(); CoUninitialize(); return ; } // 最初に見つかったビデオキャプチャデバイスのオブジェクトの // インタフェースを得る pClassEnum -> Next(1, &pMoniker, &cFetched); pMoniker -> BindToObject( 0, 0, IID_IBaseFilter, (void**)&pbf ); // ---- フィルタグラフの準備 ---- // フィルタグラフを作り、インターフェースを得る CoCreateInstance( CLSID_FilterGraph, NULL, CLSCTX_INPROC, IID_IGraphBuilder, (void **) &pGraph); pGraph -> QueryInterface( IID_IMediaControl, (LPVOID *) &pMC ); // キャプチャフィルタをフィルタグラフに追加 pGraph -> AddFilter( pbf, L"Video Capture"); // 追加を行ったのでキャプチャフィルタの参照をリリース pbf -> Release(); // ---- グラバフィルタの準備 ---- // グラバフィルタを作る CoCreateInstance( CLSID_SampleGrabber, NULL, CLSCTX_INPROC_SERVER, IID_IBaseFilter, (LPVOID *)&pF); pF -> QueryInterface( IID_ISampleGrabber, (void **)&pGrab ); // グラバフィルタの挿入場所の特定のための設定 ZeroMemory(&amt, sizeof(AM_MEDIA_TYPE)); amt.majortype = MEDIATYPE_Video; amt.subtype = MEDIASUBTYPE_RGB24; amt.formattype = FORMAT_VideoInfo; pGrab -> SetMediaType( &amt ); // グラバフィルタをフィルタグラフに追加 pGraph -> AddFilter(pF, L"SamGra"); // ---- キャプチャグラフの準備 ---- // キャプチャグラフを作る CoCreateInstance( CLSID_CaptureGraphBuilder2 , NULL, CLSCTX_INPROC, IID_ICaptureGraphBuilder2, (void **) &pCapture ); // フィルタグラフをキャプチャグラフに組み込む pCapture -> SetFiltergraph( pGraph ); // キャプチャグラフの設定、グラバをレンダリング出力に設定 pCapture -> RenderStream (&PIN_CATEGORY_PREVIEW, &MEDIATYPE_Video, pbf, NULL, pF); // ---- 表示ウィンドウの準備 ---- // ビットマップ情報の取得 pGrab -> GetConnectedMediaType( &amt ); // ビデオ ヘッダーへのポインタを獲得する。 printf( "amt.lSampleSize = %d \n", amt.lSampleSize ); VIDEOINFOHEADER *pVideoHeader = (VIDEOINFOHEADER*)amt.pbFormat; // ビデオ ヘッダーには、ビットマップ情報が含まれる。 // ビットマップ情報を BITMAPINFO 構造体にコピーする。 BITMAPINFO BitmapInfo; ZeroMemory( &BitmapInfo, sizeof(BitmapInfo) ); CopyMemory( &BitmapInfo.bmiHeader, &(pVideoHeader->bmiHeader), sizeof(BITMAPINFOHEADER)); printf( "BitmapInfo.bmiHeader.biBitCount = %d\n", BitmapInfo.bmiHeader.biBitCount ); img00.bih = BitmapInfo.bmiHeader; printf("H=%d,W=%d,size=%d\n", BitmapInfo.bmiHeader.biHeight, BitmapInfo.bmiHeader.biWidth, BitmapInfo.bmiHeader.biSizeImage); long n = img00.bih.biSizeImage; img00.lpBmpData = (BYTE *)malloc( n ); #if MTEST // allocate frame buffers frames = BitmapInfo.bmiHeader.biHeight; bmps = (BYTE**)malloc(frames * sizeof(BYTE*)); for (int i = 0; i < frames; i++) { bmps[i] = (BYTE *)malloc(n); } int ws = n / frames; #endif img00.hi = (HINSTANCE)GetWindowLong( HWND_DESKTOP, GWL_HINSTANCE ); // 表示ウィンドウの定義 WNDCLASSEX wc; // 新しくつくるウインドクラス memset( &wc, 0, sizeof(WNDCLASSEX) ); wc.cbSize = sizeof(WNDCLASSEX); wc.lpfnWndProc = grProc; // このクラスの持つウインドプロシージャ wc.hInstance = (HINSTANCE)GetWindowLong( HWND_DESKTOP, GWL_HINSTANCE ); wc.lpszClassName = "GRC0"; // このクラスの名前 wc.cbWndExtra = 10; // 関連する構造体のポインタ用のエリアを確保 RegisterClassEx( &wc ); // ウィンドクラスの登録 printf( "キャプチャを開始します、どれかキーを押して下さい\n" ); getch(); // ---- キャプチャ開始 ---- // 表示ウィンドウを作る DWORD tid; img00.hwnd = NULL; CreateThread( NULL, 0, (LPTHREAD_START_ROUTINE)th_Proc, NULL, 0, &tid ); // メッセージループのスレッドを起動 while( !(img00.hwnd) ) Sleep(10); // ウィンドウが表示されるのを待つ pGrab -> SetBufferSamples(TRUE); // グラブ開始 pMC -> Run(); // レンダリング開始 #if !MTEST int nn = 0; // グラブ回数のカウント、意味はない #endif while(1){ hr = pGrab -> GetCurrentBuffer( &n, (long *)img00.lpBmpData ); // グラブ #if MTEST memcpy(bmps[nn % frames], img00.lpBmpData, n); #if 0 // 1: for simple delay if (nn < DELAY) { memcpy(img00.lpBmpData, bmps[0], n); } else { memcpy(img00.lpBmpData, bmps[(nn - DELAY) % frames], n); } #else for (i = 0; i < frames; i++) { if (sw) { // lower to upper memcpy(img00.lpBmpData + ws * (frames - i - 1), bmps[(nn + 1 + i) % frames] + ws * (frames - i - 1), ws); } else { // upper to lower memcpy(img00.lpBmpData + ws * i, bmps[(nn + 1 + i) % frames] + ws * i, ws); } } #endif #endif // printf("グラブ %d hr = %x, \n", nn, hr); // if (!hr) nn++; InvalidateRect( img00.hwnd, NULL, FALSE); if ( kbhit() ){ #if MTEST if (getch() == 'f') { // filp! printf("flip!\n"); sw = !sw; } else { hr = pMC -> Pause(); printf( "グラブ停止:終了 -- q 、再開 -- その他のキー\n" ); if(getch() == 'q') break; pMC -> Run(); } #else getch(); // kbhitで取得したキーの破棄 hr = pMC -> Pause(); printf( "グラブ停止:終了 -- q 、再開 -- その他のキー\n" ); if(getch() == 'q') break; pMC -> Run(); #endif } #if MTEST Sleep(10); // 適当に調整すべし #else Sleep(100); #endif } pMC -> Stop(); pGrab -> SetBufferSamples( 0 ); // ---- 終了処理 ---- // インターフェースのリリースj pMC -> Release(); pDevEnum -> Release(); pGraph -> Release(); pCapture -> Release(); // COMのリリース CoUninitialize(); SendMessage( img00.hwnd, WM_CLOSE, 0, 0 ); // グラフィック画面の終了 free( img00.lpBmpData ); #if MTEST // free frame buffers for (i = 0; i < frames; i++) { free(bmps[i]); } free(bmps); #endif } // 処理部分を書きやすくするため // コンソールアプリケーションの形態をとり //   ウィンドウ関係の処理はスレッドに任せる //   そのためのスレッド側、ウィンドウ側リスト   //     動作マシンは24bitカラーであること // メッセージループのためのスレッド DWORD th_Proc( void ) { MSG msg; int sm0 = GetSystemMetrics( SM_CYCAPTION ); int sm1 = GetSystemMetrics( SM_CXDLGFRAME ); // WS_OVRELAPPの場合、枠の太さは int sm2 = GetSystemMetrics( SM_CYDLGFRAME ); // SM_C?DLGFRAMEになる // 必ずスレッドの中でウィンドウを作る img00.hwnd = CreateWindow( "GRC0", // クラスの名前 "...", WS_OVERLAPPED | WS_VISIBLE, // ウィンドウの属性 100, 100, // 表示位置 img00.bih.biWidth + sm1 * 2, // 描画サイズから大きさを計算 img00.bih.biHeight + sm0 + sm2 * 2, HWND_DESKTOP, // 親はディスクトップ NULL, img00.hi, NULL ); while( GetMessage( &msg, NULL, 0, 0 ) ){ TranslateMessage( &msg ); DispatchMessage( &msg ); } return 0; } // ウィンドウプロシージャ,再描画のみを行う LRESULT CALLBACK grProc( HWND hwnd, UINT msg, WPARAM wp, LPARAM lp ) { PAINTSTRUCT ps; switch (msg) { case WM_PAINT: // ビットマップの描画 BeginPaint( hwnd, &ps ); SetDIBitsToDevice( ps.hdc, 0, 0, // コピー先x,y座標 img00.bih.biWidth, // DIBの幅 img00.bih.biHeight, // DIBの高さ 0, 0, // DIBの座標 0, // 走査線 img00.bih.biHeight, // 走査線数 img00.lpBmpData, (BITMAPINFO *)&( img00.bih), // BITMAPINFOにキャスト DIB_RGB_COLORS ); EndPaint( hwnd, &ps ); return 0; case WM_DESTROY: // スレッドの PostQuitMessage( 0 ); // メッセージループを終了させる break; default: return DefWindowProc( hwnd, msg, wp, lp ); } return 0; } //////////