ビデオキャプチャ

 

1. 目的

パソコンのキャプチャカード、およびUSBカメラから、ビデオデータをキャプチャする。

 

2. ビデオキャプチャの使用例

ビデオ圧縮ファイルの作成、インターネットストリーミング、等。

 

3. ビデオキャプチャ関数

オペレーテイングシステムに依存し、専用の API 群が用意されている。

Windows
Video for Windows Media Control Interface ファミリ
DirectShow DirectX ファミリ (準備中)
Linux
Video4Linux (準備中)
   

 

4. 簡単なビデオキャプチャプログラム

以下は、キャプチャデバイスからのコールバックに合わせてビデオ信号をキャプチャし、ファイルに書き出すプログラムである。

Video for Windows 版: capXXX 関数群の使用、リンクオプションに vfw32.lib を加える。一連の手順として、

記号 Video for Windows の初期化とコールバック関数の指定
記号 コールバック関数を介したビデオキャプチャ (下記の例では FrameCallbackProc 関数)
記号 Video for Windows の終了処理

を基本とする。また、GUI 経由でビデオフォーマット、ビデオソースの指定、キャプチャの開始と終了を制御する。個々の cap 関数の意味は Help (MSDN) を参考のこと。

main.c (GUIプログラム 参照)

#include <stdio.h>
#include <windows.h>
#include <vfw.h>

#include "resource.h"
/************************************************************/
//	各種宣言								
/************************************************************/
#define MAX_XSIZE 720
#define MAX_YSIZE 480

// ビデオキャプチャ関数
BOOL initVideo(HWND hWnd);
BOOL closeVideo();

// Video for Windows パラメータ
HWND			hWndVideo;
CAPSTATUS		gCapStatus;
CAPTUREPARMS	gCapParms;

// RGB データ格納用バッファ
unsigned char	*pRGB;
unsigned char	*pRed;
unsigned char	*pGreen;
unsigned char	*pBlue;

// ファイル、制御フラグ
FILE*			fp;
char*			pFileName = "sample.rgb";
BOOL			bCapture = FALSE;
/************************************************************/
//	ウィンドウプログラム						
/************************************************************/
// -------------------------------------------
//	イベント処理
// -------------------------------------------
LRESULT APIENTRY 
WndProc (HWND hWnd, UINT msg, UINT wParam, LONG lParam)
{
	HMENU	hMenu;
	RECT	rMenu;

	// イベントに応じて処理を実行
	switch (msg) {
		case WM_CREATE:
			// 開始
			initVideo(hWnd);
			PostMessage(hWnd, WM_SIZE, 0, 0);
			return 0;

		case WM_CLOSE:
			// 終了
			closeVideo();
			DestroyWindow(hWnd);
			return 0;

		case WM_DESTROY:
			PostQuitMessage (0);
			break;

	    	case WM_MOVE:
		case WM_SIZE:
			// ウィンドウ調整
			capGetStatus(hWndVideo, &gCapStatus, sizeof(CAPSTATUS));
			hMenu = GetMenu(hWnd);
			GetMenuItemRect(hWndVideo, hMenu, 0, &rMenu);
			SetWindowPos(hWnd, NULL, 0, 0, 
				gCapStatus.uiImageWidth + 6, 
				gCapStatus.uiImageHeight + 25 + rMenu.bottom - rMenu.top, 
				SWP_NOZORDER | SWP_NOMOVE);
			SetWindowPos(hWndVideo, NULL, 0, 0, 
                 		gCapStatus.uiImageWidth, 
				gCapStatus.uiImageHeight, 
                 		SWP_NOZORDER | SWP_NOMOVE);
			return 0;

		case WM_COMMAND:                
			// コマンド処理
			switch (LOWORD(wParam)) {
			case ID_END:				
				// 終了
				PostMessage(hWnd, WM_CLOSE, 0, 0);
				break;
			case ID_FORMAT:				
				// ビデオフォーマット
				capDlgVideoFormat(hWndVideo);
				PostMessage(hWnd, WM_SIZE, 0, 0);
				break;
			case ID_SOURCE:				
				// ビデオソース
				capDlgVideoSource(hWndVideo);
				break;
			case ID_CAPTURE_START:				
				// キャプチャ開始
				fp = fopen(pFileName, "wb");
				if(fp == NULL) {
					MessageBox(hWnd, "ファイルオープンエラー", "ファイルオープンエラー", MB_OK);
					PostMessage(hWnd, WM_CLOSE, 0, 0);
					bCapture = FALSE;
					break;
				}
				MessageBox(hWnd, "キャプチャを開始します", "キャプチャ開始", MB_OK);
				bCapture = TRUE;
				break;
			case ID_CAPTURE_END:				
				// キャプチャ終了
				bCapture = FALSE;
				fclose(fp);
				MessageBox(hWnd, "キャプチャを終了しました", "キャプチャ終了", MB_OK);
				break;
			}
			return 0;
	}
	return DefWindowProc (hWnd, msg, wParam, lParam);
}

// -------------------------------------------
//	メイン制御部
// -------------------------------------------
int PASCAL
WinMain(HINSTANCE hInst, HINSTANCE hDummy, LPSTR lpStr, int nShowCmd)
{
	WNDCLASS 	wc;
	MSG		msg;
	HWND		hWnd;

	// ウィンドウクラスの定義
	wc.lpfnWndProc = WndProc;
	wc.hInstance = hInst;
	wc.hIcon = LoadIcon (NULL, IDI_APPLICATION);
	wc.hCursor = LoadCursor (NULL, IDC_ARROW);
	wc.lpszClassName = "Capture";
	wc.hbrBackground = GetStockObject(WHITE_BRUSH);
	wc.style = 0;
	wc.cbClsExtra = 0;
	wc.cbWndExtra = 0;
	wc.lpszMenuName = MAKEINTRESOURCE(IDR_MENU1);
	RegisterClass (&wc);

    	// メインウィンドウの生成
	hWnd = CreateWindow(
		wc.lpszClassName, 
		"Video Capture Sample", 
		WS_OVERLAPPEDWINDOW,
		CW_USEDEFAULT, 
		CW_USEDEFAULT,
		256,
		256,
     		0, 
		0, 
		hInst, 
		0
    	);
	if (hWnd == NULL) ExitThread (1);
	ShowWindow (hWnd, SW_SHOWNOACTIVATE);
	UpdateWindow (hWnd);

	// 無限ループ (イベントが発生するたびに WndProc を呼び出す)
	while (GetMessage (&(msg), NULL, 0, 0)) {
		TranslateMessage(&msg);
		DispatchMessage (&msg);
	}

	return msg.wParam;
}
/************************************************************/
//	Video for Windows プログラム					
/************************************************************/
// -------------------------------------------
//	FrameCallbackProc
// -------------------------------------------
LRESULT PASCAL 
FrameCallbackProc(HWND hWnd, LPVIDEOHDR lpVHdr) { 
	BITMAPINFOHEADER nFormat;
	DWORD	dwFormatSize;
	int nWidth, nHeight, nBitCount;

	int i, j;
	unsigned char *rgb;
	unsigned char *r, *g, *b;

	// フラグ制御
	if(bCapture == FALSE) return TRUE;

	// ピクチャパラメータの取得
	dwFormatSize = capGetVideoFormatSize(hWndVideo);
	if( !capGetVideoFormat(hWndVideo, &nFormat, dwFormatSize) ) {
		DestroyWindow(hWndVideo);
		return FALSE;
	}
	nWidth = nFormat.biWidth;
	nHeight = nFormat.biHeight;
	nBitCount = nFormat.biBitCount;
	if(nWidth > MAX_XSIZE || nHeight > MAX_YSIZE) {
		MessageBox(hWnd, "サイズが大きすぎます", "キャプチャエラー", MB_OK);
		bCapture = FALSE;
		return FALSE ; 
	}

	// (width, height) を 16 の倍数に変換
	if(nWidth % 16 != 0) nWidth = (nWidth / 16 + 1) * 16;
	if(nHeight % 16 != 0) nHeight = (nHeight / 16 + 1) * 16;
	
	// キャプチャ実行
	switch (nFormat.biCompression) {
	case BI_RGB:
		// RGB 24 ビットキャプチャのみサポート
		switch(nBitCount) {
		case 24: 
			// キャプチャカードからメモリにコピー
			memcpy(pRGB, lpVHdr->lpData, lpVHdr->dwBufferLength);

			// RGB データの並び替え(BMP フォーマットは上下反転してるため)
			r = pRed;
			g = pGreen;
			b = pBlue;
			for(j=0; j<nHeight; j++) for(i=0; i<nWidth; i++) {
				rgb = pRGB + 3*((nHeight-1-j)*nWidth+i); 
				*(r++) = *(rgb+2); 
				*(g++) = *(rgb+1); 
				*(b++) = *(rgb+0); 
			}
			
			// キャプチャデータのファイル書出し
			fwrite(pRed,   1, nWidth*nHeight, fp);
			fwrite(pGreen, 1, nWidth*nHeight, fp);
			fwrite(pBlue,  1, nWidth*nHeight, fp);

			break;
		default: 
			MessageBox(hWnd, "24bit RGB しかキャプチャできません", "キャプチャエラー", MB_OK);
			bCapture = FALSE;
			return FALSE ; 
		}
		break;
	default:
		MessageBox(hWnd, "24bit RGB しかキャプチャできません", "キャプチャエラー", MB_OK);
		bCapture = FALSE;
		return FALSE ; 
	}

	return TRUE ; 
} 

// -------------------------------------------
//	ErrorCallbackProc
// -------------------------------------------
LRESULT CALLBACK 
ErrorCallbackProc(HWND hWnd, int nErrID, LPSTR lpErrorTxt)
{
	char    szText[256];

	if (nErrID == 0) return TRUE;
    	if (nErrID == IDS_CAP_DRIVER_ERROR) {
        	// 起動時のエラー
        	MessageBeep(MB_ICONEXCLAMATION);
        	MessageBox(hWnd, "初期化エラー", "初期化エラー", MB_OK | MB_ICONEXCLAMATION);
		PostMessage(hWndVideo, WM_CLOSE, 0, 0);
    	} else {
        	// その他のエラー
        	wsprintf(szText, "%s(%d)", lpErrorTxt, nErrID);
        	MessageBeep(MB_ICONEXCLAMATION);
        	MessageBox(hWndVideo, szText, "キャプチャエラー", MB_OK | MB_ICONEXCLAMATION);
    	}
    	return TRUE;
}

// -------------------------------------------
//	初期設定
// -------------------------------------------
int    
initVideo(HWND hWnd)
{
    	char	achDeviceName[80];
    	char	achDeviceVersion[80];
    	WORD	wDriverCount = 0;
    	WORD	wIndex;

	BITMAPINFOHEADER nFormat;
	DWORD	dwFormatSize;
	int	nWidth, nHeight;

    	// メモリ領域の確保
	pRGB =   (unsigned char *) malloc(MAX_XSIZE * MAX_YSIZE * 3);
	pRed =   (unsigned char *) malloc(MAX_XSIZE * MAX_YSIZE);
	pGreen = (unsigned char *) malloc(MAX_XSIZE * MAX_YSIZE);
	pBlue =  (unsigned char *) malloc(MAX_XSIZE * MAX_YSIZE);
	if(pRGB == NULL || pRed == NULL || pGreen == NULL || pBlue == NULL) return FALSE;

    	// キャプチャウィンドウの生成
    	hWndVideo = capCreateCaptureWindow("Capture", 
                WS_CHILD | WS_VISIBLE, 0, 0, 160, 120, (HWND) hWnd, (int) 0);
	if(hWndVideo == NULL) return FALSE;

    	// コールバック関数の設定
	capSetCallbackOnError(hWndVideo, ErrorCallbackProc);
	capSetCallbackOnFrame(hWndVideo, FrameCallbackProc);

    	// ドライバの常駐
	for (wIndex = 0; wIndex < 10; wIndex++) {
      	if (capGetDriverDescription(wIndex, 
						   (LPSTR) achDeviceName, 
						    sizeof(achDeviceName), 
						   (LPSTR) achDeviceVersion, 
						    sizeof(achDeviceVersion))) {
			if (wDriverCount++ == 0) capDriverConnect(hWndVideo, wIndex);
        	} 
    	}

 	// ピクチャフォーマットの取得
	dwFormatSize = capGetVideoFormatSize(hWndVideo);
	if( !capGetVideoFormat(hWndVideo, &nFormat, dwFormatSize) ) {
	    	DestroyWindow(hWndVideo);
		return FALSE;
	}
	nWidth = nFormat.biWidth;
	nHeight = nFormat.biHeight;
	if(nWidth > MAX_XSIZE || nHeight > MAX_YSIZE) {
		MessageBox(hWnd, "サイズが大きすぎます", "キャプチャエラー", MB_OK);
		return FALSE ; 
	}
	if(nFormat.biCompression != BI_RGB || nFormat.biBitCount != 24) {
		MessageBox(hWnd, "24bit RGB しかキャプチャできません", "キャプチャエラー", MB_OK);
		return FALSE;
	}

    	// 各種キャプチャパラメータの取得と設定
    	capCaptureGetSetup(hWndVideo, &gCapParms, sizeof(CAPTUREPARMS));
    	gCapParms.dwRequestMicroSecPerFrame = (DWORD)1000000 / 15;
    	gCapParms.fMakeUserHitOKToCapture   = TRUE;
    	gCapParms.wPercentDropForError      = 100;
    	gCapParms.fYield                    = FALSE;
    	gCapParms.fLimitEnabled             = TRUE;
    	gCapParms.wTimeLimit                = 10;
    	gCapParms.fCaptureAudio             = FALSE;
    	gCapParms.fUsingDOSMemory           = FALSE;
    	gCapParms.vKeyAbort                 = VK_ESCAPE;
    	gCapParms.fAbortLeftMouse           = TRUE;
    	gCapParms.fAbortRightMouse          = TRUE;
    	gCapParms.fMCIControl               = FALSE;
    	gCapParms.fStepMCIDevice            = FALSE;

    	capPreviewRate(hWndVideo, 66);
    	capPreview(hWndVideo, TRUE);
    	capOverlay(hWndVideo, TRUE);
    	capCaptureSetSetup(hWndVideo, &gCapParms, sizeof(CAPTUREPARMS));

    	return TRUE;
}

// -------------------------------------------
//	終了処理
// -------------------------------------------
BOOL
closeVideo(void) {
    	// コールバック関数の解放
    	capSetCallbackOnError(hWndVideo, NULL);
    	capSetCallbackOnFrame(hWndVideo, NULL);

	// メモリ領域の解放
	free(pRGB);
	free(pRed);
	free(pGreen);
	free(pBlue);

    	// キャプチャウィンドウの終了
    	DestroyWindow(hWndVideo);

	return TRUE;
}

resource.rc: ウィンドウにGUI コンポーネント (リソース) を被せるためにプロジェクトに組み込むスクリプトファイル。実際は、VisualC++ 上でリソースエディタを使うことで、グラフィカルに編集できる。また、このとき、resource.h が自動生成される。

//Microsoft Developer Studio generated resource script.
//
#include "resource.h"
#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "afxres.h"
/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
// 日本語 resources
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_JPN)
#ifdef _WIN32
LANGUAGE LANG_JAPANESE, SUBLANG_DEFAULT
#pragma code_page(932)
#endif //_WIN32
/////////////////////////////////////////////////////////////////////////////
//
// Menu
//
IDR_MENU1 MENU DISCARDABLE
BEGIN
POPUP "ファイル"
BEGIN
MENUITEM "終了", ID_END
END
POPUP "設定"
BEGIN
MENUITEM "ビデオフォーマット", ID_FORMAT
MENUITEM "ビデオソース", ID_SOURCE
END
POPUP "キャプチャ"
BEGIN
MENUITEM "開始", ID_CAPTURE_START
MENUITEM "終了", ID_CAPTURE_END
END
END

#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//
1 TEXTINCLUDE DISCARDABLE
BEGIN
"resource.h\0"
END
2 TEXTINCLUDE DISCARDABLE
BEGIN
"#include ""afxres.h""\r\n"
"\0"
END
3 TEXTINCLUDE DISCARDABLE
BEGIN
"\r\n"
"\0"
END
#endif // APSTUDIO_INVOKED
#endif // 日本語 resources
/////////////////////////////////////////////////////////////////////////////


#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//
/////////////////////////////////////////////////////////////////////////////
#endif // not APSTUDIO_INVOKED

 

 

Windows DirectShow 版: 準備中

Linux Video4Linux 版: 準備中