ビデオ表示

 

1. 目的

ディスプレイ上にビデオデータを表示させる。

 

2. ビデオ表示の使用例

画像表示、ビデオプレーヤ、インターネットストリーミング。

 

3. ビデオ表示関数

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

Windows
Win32 Win32 API
DirectDraw DirectX ファミリ
Linux
X11 X11 API
ほか  

 

4. 簡単なビデオ表示プログラム

以下は、一定周期毎にファイルからRGBデータを読出し、ディスプレイ表示させるプログラムである。一連の手順として、

表示デバイスの初期化
タイムイベントとRGBデータのディスプレイ表示
表示デバイスの終了処理

を基本とする。

 

Windows DirectDraw 版: DirectDraw 関数群の使用、リンクオプションに ddraw.lib を加える。

DirectDraw プログラムはC++で記述されるため、その他の部分をCで記述する場合は、CとC++を共存させるルールに従わなければならない。defs.h に書かれた #ifdef __cplusplus ... #endif の記述がこれに相当する。

drawDD() 関数に出てくるサーフェスのロック、アンロックとは、ロック期間中は他のプログラムからの書きこみを許さないと言う意味で、マルチスレッドの排他制御に相当する。なお、サーフェスとは、ビデオカードのビデオメモリに相当する。ddrawDD() 関数の最後の Blt() 関数が表示を実行する。

また、下記のプログラムでは、Win32 のSetTimer()、WM_TIMER の組合せによるタイムイベントを使っている。100ms毎にファイルからRGBデータを読みこみ、drawDD() 関数を呼び出して表示を行っている。

なお、下記のプログラムは24ビットフルカラー、および前回のビデオキャプチャで作成したRGBデータの形式しか正しく表示しないので注意すること。

 

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

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

#include "defs.h"

/************************************************************/
//	各種宣言								
/************************************************************/
// RGB データ格納用バッファ
unsigned char	*pRed;
unsigned char	*pGreen;
unsigned char	*pBlue;

// ファイルと制御フラグ
FILE*			fp;
char*			pFileName = "sample.rgb";
BOOL			bWrite = FALSE;

// 画像のサイズ (キャプチャ時の設定に合わせる)
int			nWidth = 176;
int			nHeight = 144;

/************************************************************/
//	ウィンドウプログラム					
/************************************************************/
// -------------------------------------------
//	イベント処理
// -------------------------------------------
LRESULT APIENTRY 
WndProc (HWND hWnd, UINT msg, UINT wParam, LONG lParam)
{
	BOOL ret;

	// イベントに応じて処理を実行
	switch (msg) {
	    case WM_CREATE:
		    // メモリ領域の確保
			pRed =   (unsigned char *) malloc(nWidth * nHeight);
			pGreen = (unsigned char *) malloc(nWidth * nHeight);
			pBlue =  (unsigned char *) malloc(nWidth * nHeight);
			if(pRed == NULL || pGreen == NULL || pBlue == NULL) {
				MessageBox(hWnd, "メモリの確保に失敗しました", "エラー", MB_OK);
				PostMessage(hWnd, WM_CLOSE, 0, 0);
				return 0;
			}

			// 開始
			initDD(hWnd, nWidth, nHeight);
			fp = fopen(pFileName, "rb");
			if(fp == NULL) {
				MessageBox(hWnd, "ファイルのオープンに失敗しました", "エラー", MB_OK);
				PostMessage(hWnd, WM_CLOSE, 0, 0);
				return 0;
			}
			SetTimer(hWnd, 1, 100, NULL);
			bWrite = TRUE;
			return 0;
		case WM_CLOSE:
			// 終了
			bWrite = FALSE;
			fclose(fp);
			closeDD();

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

			DestroyWindow(hWnd);
			return 0;

		case WM_DESTROY:
			PostQuitMessage (0);
			break;

		case WM_TIMER:
			if(bWrite == TRUE) {
				if(feof(fp)) fseek(fp, 0, SEEK_SET);
				fread(pRed, 1, nWidth*nHeight, fp);
				fread(pGreen, 1, nWidth*nHeight, fp);
				fread(pBlue, 1, nWidth*nHeight, fp);
				drawDD(hWnd, nWidth, nHeight, pRed, pGreen, pBlue);
			}
			break;
	}

	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 = "Player";
	wc.hbrBackground = GetStockObject(WHITE_BRUSH);
	wc.style = 0;
	wc.cbClsExtra = 0;
	wc.cbWndExtra = 0;
	wc.lpszMenuName = 0;
	RegisterClass (&wc);

    	// メインウィンドウの生成
	hWnd = CreateWindow(
       		wc.lpszClassName, 
			"Video Player Sample", 
			WS_OVERLAPPEDWINDOW,
			CW_USEDEFAULT, 
			CW_USEDEFAULT,
			nWidth,
			nHeight,
      		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;
}

 

ddraw.cpp

#include <windows.h>
#include <ddraw.h>

#include "defs.h"

// DirectDraw 
LPDIRECTDRAW		lpDDraw; 
LPDIRECTDRAWCLIPPER 	lpClipper;
LPDIRECTDRAWSURFACE 	lpPrimary; 
LPDIRECTDRAWSURFACE 	lpMain;

//-------------------------------------------
//	初期化
//-------------------------------------------
BOOL
initDD(HWND hWnd, int width, int height) {
	HRESULT         ddreturn;	
	DDSURFACEDESC   ddsd;

	// DirectDraw オブジェクトの生成
	ddreturn = DirectDrawCreate( NULL, &lpDDraw, NULL );
	if ( ddreturn != DD_OK ) {
		MessageBox(hWnd, "DirectDrawオブジェクトの生成に失敗しました", "エラー", MB_OK);
      	return FALSE;
	}

	// DirectDraw 協調レベルの設定
    	lpDDraw->SetCooperativeLevel( hWnd, DDSCL_NORMAL );

	// DirectDraw クリッパーの生成
	lpDDraw->CreateClipper(0, &lpClipper, NULL);
	lpClipper->SetHWnd(0, hWnd);

	// DirectDraw サーフェスの生成 (Primary)
	ddsd.dwSize = sizeof( ddsd );
	ddsd.dwFlags = DDSD_CAPS;
	ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
	lpDDraw->CreateSurface( &ddsd, &lpPrimary, NULL );

	// クリッパ設定
	lpPrimary->SetClipper(lpClipper);

	// DirectDraw サーフェスの生成 (Main)
	ddsd.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH;
	ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;
	ddsd.dwHeight = height;
	ddsd.dwWidth = width;
	lpDDraw->CreateSurface ( &ddsd, &lpMain, NULL );

	// ビデオカードのフォーマットの取得、設定
	DDPIXELFORMAT pixelFmt;
	memset (&pixelFmt, 0, sizeof(pixelFmt));
	pixelFmt.dwSize = sizeof(pixelFmt);
	lpPrimary->GetPixelFormat (&pixelFmt);
	if (pixelFmt.dwFlags & DDPF_RGB) {
		if (pixelFmt.dwRGBBitCount == 24) {
			if ((pixelFmt.dwRBitMask == 0xff0000) &&
				(pixelFmt.dwGBitMask == 0x00ff00) &&
				(pixelFmt.dwBBitMask == 0x0000ff)) { 
				// OK: 24bitフルカラーモード
			} else {
				MessageBox(hWnd, "24bitフルカラーにしてください", "エラー", MB_OK);
				return FALSE;
			}
		} else {
			MessageBox(hWnd, "24bitフルカラーにしてください", "エラー", MB_OK);
			return FALSE;
		}
	} else {
		MessageBox(hWnd, "24bitフルカラーにしてください", "エラー", MB_OK);
		return FALSE;
	}

	return TRUE;
}

//-------------------------------------------
//	終了処理
//-------------------------------------------
BOOL
closeDD() {
	HRESULT ddreturn;

	// DirectDraw オブジェクト開放
    	if ( lpMain != NULL ) {
        	ddreturn = lpMain->Release();
		lpMain = NULL;
	}
    	if ( lpPrimary != NULL ) {
        	ddreturn = lpPrimary->Release();
		lpPrimary = NULL;
	}
	lpDDraw = NULL;

	return TRUE;
}

//-------------------------------------------
//	DirectDraw 表示
//-------------------------------------------
BOOL 
restoreDD( void )
{
	lpPrimary->Restore();
	lpMain->Restore();

	return TRUE;
}

BOOL 
drawDD(HWND hWnd, int width, int height, unsigned char *red, unsigned char *green, unsigned char *blue) {
	HRESULT		ddreturn;
    	DDSURFACEDESC	ddsd;

	RECT			rect;
	RECT			writeRect;
    	POINT			pt;

	int			i, j;
	unsigned char	r0, r1, r2, r3;
	unsigned char	g0, g1, g2, g3;
	unsigned char	b0, b1, b2, b3;
	unsigned char	*pr, *pg, *pb;
	unsigned char	*ds;
	unsigned int	*ids;

	// サーフェスパラメータの設定
	memset( &ddsd, 0, sizeof(ddsd) );
	ddsd.dwSize = sizeof( ddsd );
	ddsd.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH;
	ddsd.dwWidth = width;
	ddsd.dwHeight = height;

	// サーフェスのロック
	lpMain->Lock(NULL, &ddsd, 0, NULL);

	// サーフェスへのRGBデータの書きこみ
	pr = red;
	pg = green;
	pb = blue;
	ds = (unsigned char*) ddsd.lpSurface;
	for (j=0; j<height; j++) {
		ids = (unsigned int *) ds;
		for (i=0; i<width; i+=4) {
			r0 = *(pr++); r1 = *(pr++); r2 = *(pr++); r3 = *(pr++); 
			g0 = *(pg++); g1 = *(pg++); g2 = *(pg++); g3 = *(pg++); 
			b0 = *(pb++); b1 = *(pb++); b2 = *(pb++); b3 = *(pb++); 
			*(ids++) = b0 | (g0 << 8) | (r0 << 16) | (b1 << 24);	
			*(ids++) = g1 | (r1 << 8) | (b2 << 16) | (g2 << 24);	
			*(ids++) = r2 | (b3 << 8) | (g3 << 16) | (r3 << 24);	
		}
		ds += ddsd.lPitch;
	}

	// サーフェスのアンロック
	lpMain->Unlock(NULL);

	// 書き出し位置の調整と表示
	SetWindowPos(hWnd, NULL, 0, 0, width + 6, height + 25, SWP_NOZORDER | SWP_NOMOVE);
	GetClientRect(hWnd, &rect );
	pt.x = pt.y = 0;
	ClientToScreen(hWnd, &pt );
	SetRect (&writeRect, pt.x, pt.y, width + pt.x, height + pt.y);
	SetRect (&rect, 0, 0, width, height);
	ddreturn = lpPrimary->Blt(&writeRect, lpMain, &rect, DDBLT_ASYNC, NULL);
	if ( ddreturn == DDERR_SURFACELOST ) restoreDD();

	return TRUE;
}
 

 

defs.h: CプログラムとC++プログラムの共存のために使用

#ifdef __cplusplus
extern "C" {
#endif

// DirectDraw 関数
BOOL	initDD(HWND hWnd, int width, int height);
BOOL	drawDD(HWND hWnd, int width, int height, unsigned char *r, unsigned char *g, unsigned char *b);
BOOL	closeDD();

#ifdef __cplusplus
}
#endif 

 

Windows Win32 版: 準備中

Linux X11 版: 準備中