動画ファイル(AVI)を表示/再生する例題です。Video for Windows(VFW)によりAVIファイルの読込みを行っているため、他のOSの移植等は全く考慮していません(一から読込みのは大変だったので..)。AVIから任意の再生位置の画像を取得することができますので、各画像(BMP)に対する画像処理も可能になるかと思います。
コーデック(divx,xvid etc.)による画像の取得も行っていますが、実行するコンピュータにコーデックがインストールされていないと表示されません。目的のコーデックを他のサイトからダウンロードする必要があります。コーデックがインストールされているかどうかを確認したい場合は、[コントロールパネル]-[管理ツール]-[コンピュータの管理]-[デバイスマネージャ]-[サウンド、ビデオ、およびゲームコントローラ]-[オーディオ(ビデオ)CODEC](XPの場合)において目的のコーデックがあるか確認してください。
実行ファイル(aviplay.exe)
//----------------------------------------------------------------- // aviplay.cpp: // AVI 再生/表示ウィンドウ // Last Update: <2005/01/28 11:21:26 A.Murakami> //----------------------------------------------------------------- #include <stdio.h> #include <math.h> #include <windows.h> #include <vfw.h> #include "aviplay.h" //----------------------------------------------------------------- #pragma comment(linker,"/subsystem:windows") #pragma comment(lib,"winmm") #pragma comment(lib,"vfw32") //----------------------------------------------------------------- // グローバル変数定義 //----------------------------------------------------------------- HINSTANCE hAppInst; HWND hAppWnd; UINT aviLoad,iWidth,iHeight,iSize; LPBYTE lpBMP = NULL; AviInfo avi; double wm_xscale,wm_yscale; static HWND hwB0,hwB1,hwB2,hwB3; //----------------------------------------------------------------- UINT vRun=FALSE; // 連続再生用 //----------------------------------------------------------------- void TRACE(char *fmt,...) { char szBuff[BUFSIZ]; va_list ap; va_start(ap,fmt); vsprintf(szBuff,fmt,ap); #ifdef _MSC_VER OutputDebugString(szBuff); #else fprintf(stderr,szBuff); #endif va_end(ap); } //----------------------------------------------------------------- int WINAPI WinMain(HINSTANCE hCurInst, HINSTANCE hPrevInst, LPSTR lpsCmdLine, int nCmdShow) { MSG msg; LPCTSTR szClassName = "aviplay"; // 初期化 aviLoad = FALSE; iWidth = 300; iHeight = 200; // ウィンドウ・クラスの登録 if(!hPrevInst){ if(!InitApp(hCurInst,szClassName)) return 0; hAppInst = hCurInst; } // ウィンドウ作成 if(!InitInstance(hCurInst,szClassName,lpsCmdLine,nCmdShow)){ return 0; } // AVI 初期化 AviInit(); // メッセージループ while(GetMessage(&msg,NULL,0,0)){ TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; } //----------------------------------------------------------------- // ウィンドウ・クラスの登録 //----------------------------------------------------------------- BOOL InitApp(HINSTANCE hInst, LPCSTR szClassName) { WNDCLASSEX wc; wc.cbSize = sizeof(wc); wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = WndProc; // プロシージャ名 wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInst; // インスタンス wc.hIcon = LoadIcon(NULL,IDI_APPLICATION); wc.hCursor = LoadCursor(NULL,IDC_ARROW); wc.hIconSm = LoadIcon(NULL,IDI_APPLICATION); wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); wc.lpszMenuName = NULL; // メニュー名 wc.lpszClassName = (LPCSTR)szClassName; return (RegisterClassEx(&wc)); } //----------------------------------------------------------------- // ウィンドウの生成 //----------------------------------------------------------------- BOOL InitInstance(HINSTANCE hInst,LPCSTR szClassName, LPSTR lpsCmdLine,int nCmdShow) { TCHAR lpStr[256]; HWND hWnd; // ウィンドウの作成 wsprintf(lpStr,"動画ファイル(AVI)再生"); hWnd = CreateWindow(szClassName,lpStr, // タイトルバー WS_OVERLAPPEDWINDOW, // ウィンドウ CW_USEDEFAULT, // X座標 CW_USEDEFAULT, // Y座標 15+iWidth, // 幅 65+iHeight, // 高さ NULL, // 親ウィンドウのハンドル NULL, // メニューハンドル hInst, // インスタンスハンドル NULL); if(!hWnd) return FALSE; ShowWindow(hWnd,nCmdShow); UpdateWindow(hWnd); hAppWnd = hWnd; return TRUE; } //----------------------------------------------------------------- // メッセージ情報 //----------------------------------------------------------------- LRESULT CALLBACK WndProc(HWND hwnd,UINT iMsg,WPARAM wParam,LPARAM lParam) { switch(iMsg){ case WM_CHAR: if(wParam=='q') SendMessage(hwnd,WM_DESTROY,0,0); break; case WM_CREATE: { // ボタン作成 hwB0 = CreateWindow("Button","開く",WS_CHILD | WS_VISIBLE, 5,5,55,25,hwnd,(HMENU)ID_OPEN,hAppInst,NULL); hwB1 = CreateWindow("Button","コマ送り",WS_CHILD | WS_VISIBLE, 60,5,70,25,hwnd,(HMENU)ID_AVI1,hAppInst,NULL); hwB2 = CreateWindow("Button","再生",WS_CHILD | WS_VISIBLE, 130,5,55,25,hwnd,(HMENU)ID_AVI2,hAppInst,NULL); hwB3 = CreateWindow("Button","保存",WS_CHILD | WS_VISIBLE, 185,5,55,25,hwnd,(HMENU)ID_SAVE,hAppInst,NULL); } break; case WM_COMMAND: switch(LOWORD(wParam)){ case 'q': // 終了 SendMessage(hwnd,WM_DESTROY,0,0); break; case ID_OPEN: // ファイル選択 if(vRun) break; if(OpenFile(avi.in_path)){ AviOpen(); } break; case ID_AVI1: // コマ送り if(aviLoad) AviNext(); break; case ID_AVI2: // 連続再生 if(aviLoad) AviView(); break; case ID_SAVE: // 画像保存 static TCHAR szFn[MAX_PATH]; if(SaveFile(szFn)){ SaveBMP(szFn,lpBMP); } break; } break; case WM_PAINT: if(aviLoad){ HDC hdc; PAINTSTRUCT ps; // DIBを画面に描画 hdc = BeginPaint(hwnd,&ps); StretchDIBits(hdc,4,35, (int)(wm_xscale*iWidth),(int)(wm_yscale*iHeight), 0,0,iWidth,iHeight, lpBMP+avi.biSize,(LPBITMAPINFO)lpBMP, DIB_RGB_COLORS,SRCCOPY); EndPaint(hwnd,&ps); } break; case WM_SIZE: { // ウィンドウ幅の変更 UINT wx,wy; WINDOWPLACEMENT wndpl; GetWindowPlacement(hAppWnd,&wndpl); wx = wndpl.rcNormalPosition.right - wndpl.rcNormalPosition.left; wy = wndpl.rcNormalPosition.bottom - wndpl.rcNormalPosition.top; wx -= 14; wy -= 84; wm_xscale = wx/(double)iWidth; wm_yscale = wy/(double)iHeight; // 再描画範囲 SetRect(&avi.dRgn, 4,35,4+(int)(iWidth*wm_xscale),35+(int)(iHeight*wm_yscale)); } break; case WM_DESTROY: // メモリの解放 if(aviLoad) AviClose(); PostQuitMessage(0); break; } return DefWindowProc(hwnd,iMsg,wParam,lParam); } //----------------------------------------------------------------- // ウィンドウタイトルの設定 //----------------------------------------------------------------- void SetWTitle(LPTSTR tstr) { SetWindowText(hAppWnd,tstr); } char *getfps(void) { static DWORD last = 0; static DWORD frames = 0; static char buf[256] = ""; DWORD current = timeGetTime(); frames++; if(500 <= current - last) { // 0.5秒毎に更新 double dt = (double)(current - last) / 1000.0f; double fps = (double)frames / dt; last = current; frames = 0; sprintf(buf,"%.02f fps",fps); } return buf; } //----------------------------------------------------------------- // 画像情報の設定 //----------------------------------------------------------------- void set_window(LPBITMAPINFOHEADER bih) { TCHAR tStr[256]; // 画像情報 iWidth = bih->biWidth; iHeight = bih->biHeight; // ウィンドウ幅の変更 UINT wx,wy; WINDOWPLACEMENT wndpl; GetWindowPlacement(hAppWnd,&wndpl); wx = wndpl.rcNormalPosition.left; wy = wndpl.rcNormalPosition.top; MoveWindow(hAppWnd,wx,wy,15+iWidth,65+iHeight,TRUE); // キャプション wsprintf(tStr,"[%s] frame %d/%d",avi.in_path,1,avi.dwLength); SetWTitle(tStr); // 描画サイズ wm_xscale = wm_yscale = 1.0; } //----------------------------------------------------------------- // [開く]ファイルの選択 //----------------------------------------------------------------- UINT OpenFile(LPTSTR lpFname) { static OPENFILENAME ofn; TCHAR szFt[MAX_PATH]; lpFname[0] = NULL; // ファイル[開く]ダイアログ設定 ofn.lStructSize = sizeof(OPENFILENAME); ofn.hwndOwner = hAppWnd; ofn.hInstance = NULL; ofn.lpstrFilter = "AVI stream(*.avi)\0*.avi\0" "すべてのファイル(*.*)\0*.*\0\0"; ofn.lpstrCustomFilter = NULL; ofn.nMaxCustFilter = 0; ofn.nFilterIndex = 1; // フィルタの初期位置 ofn.lpstrFile = lpFname; // ファイル名用文字列バッファ ofn.nMaxFile = 255; // 文字列バッファのサイズ ofn.lpstrFileTitle = szFt; // タイトル用文字列バッファ ofn.nMaxFileTitle = 255; // 文字列バッファのサイズ ofn.lpstrInitialDir = NULL; ofn.lpstrTitle = "動画ファイル(AVI)の選択"; ofn.Flags = OFN_FILEMUSTEXIST | OFN_HIDEREADONLY; ofn.nFileOffset = 0; ofn.nFileExtension = 0; ofn.lpstrDefExt = "avi"; ofn.lCustData = 0; ofn.lpfnHook = NULL; ofn.lpTemplateName = NULL; // ダイアログを開く if(!GetOpenFileName(&ofn)) return FALSE; return TRUE; } //----------------------------------------------------------------- // [保存]ファイルの選択 //----------------------------------------------------------------- UINT SaveFile(LPTSTR lpFname) { static OPENFILENAME ofn; TCHAR szFt[MAX_PATH]; lpFname[0] = NULL; // ファイル[保存]ダイアログ設定 ofn.lStructSize = sizeof(OPENFILENAME); ofn.hwndOwner = hAppWnd; ofn.hInstance = NULL; ofn.lpstrFilter = "Bitmap(*.bmp)\0*.bmp\0" "すべてのファイル(*.*)\0*.*\0\0"; ofn.lpstrCustomFilter = NULL; ofn.nMaxCustFilter = 0; ofn.nFilterIndex = 1; // フィルタの初期位置 ofn.lpstrFile = lpFname; // ファイル名用文字列バッファ ofn.nMaxFile = 255; // 文字列バッファのサイズ ofn.lpstrFileTitle = szFt; ofn.nMaxFileTitle = 255; // 文字列バッファのサイズ ofn.lpstrInitialDir = NULL; ofn.lpstrTitle = "画像(BMP)の保存"; ofn.Flags = OFN_OVERWRITEPROMPT | OFN_HIDEREADONLY; ofn.nFileOffset = 0; ofn.nFileExtension = 0; ofn.lpstrDefExt = "bmp"; ofn.lCustData = 0; ofn.lpfnHook = NULL; ofn.lpTemplateName = NULL; // ダイアログを開く if(!GetSaveFileName(&ofn)) return FALSE; return TRUE; } //----------------------------------------------------------------- // ビットマップの保存 //----------------------------------------------------------------- UINT SaveBMP(LPCTSTR lpFname,LPBYTE lpBuf) { HANDLE hdlBmp; BITMAPFILEHEADER bfhBmp; LPBITMAPINFOHEADER bih = (LPBITMAPINFOHEADER)lpBuf; LONG offset = sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER); LONG cpsize = bih->biClrUsed*sizeof(RGBQUAD); UINT bCnt = bih->biBitCount,bPln = bih->biPlanes; DWORD dwdPixelsSize,wSize; // 画像サイズ dwdPixelsSize = ((bih->biWidth*bCnt+31)&~0x1f)*bPln/8; dwdPixelsSize *= bih->biHeight; // ファイルを開く hdlBmp = CreateFile(lpFname,GENERIC_WRITE,0,NULL,OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL,NULL); if(hdlBmp==INVALID_HANDLE_VALUE){ MessageBox(NULL,"書込みに失敗しました",NULL,MB_OK); return FALSE; } // ヘッダ情報 bfhBmp.bfType = 'M'*256+'B'; bfhBmp.bfSize = offset + cpsize + dwdPixelsSize; bfhBmp.bfReserved1 = 0; bfhBmp.bfReserved2 = 0; bfhBmp.bfOffBits = offset + cpsize; // 書込み dwdPixelsSize += sizeof(BITMAPINFOHEADER) + cpsize; WriteFile(hdlBmp,&bfhBmp,sizeof(BITMAPFILEHEADER),&wSize,NULL); WriteFile(hdlBmp,lpBuf,sizeof(BYTE)*dwdPixelsSize,&wSize,NULL); // 後片付け CloseHandle(hdlBmp); return TRUE; } //----------------------------------------------------------------- // AVI 初期化 //----------------------------------------------------------------- void AviInit() { avi.in_file = NULL; avi.in_video = NULL; avi.in_frame = NULL; avi.lpSrcFmt = NULL; avi.lpDecFmt = NULL; avi.lpSrc = NULL; lpBMP = NULL; avi.codec = FALSE; } //----------------------------------------------------------------- // AVIのオープン //----------------------------------------------------------------- int AviOpen() { AVIFILEINFO pfi; LPBITMAPINFOHEADER bih; //-------------------------------------------------- // Video for Windows(VFW) によるAVIのオープン //-------------------------------------------------- if(aviLoad) AviClose(); AVIFileInit(); if(AVIFileOpen(&avi.in_file,avi.in_path,OF_READ,NULL)){ TRACE("open err.\n"); return FALSE; } //-------------------------------------------------- // 映像情報 //-------------------------------------------------- AVIFileInfo(avi.in_file,&pfi,sizeof(AVIFILEINFO)); if(AVIFileGetStream(avi.in_file,&avi.in_video,streamtypeVIDEO,0)){ TRACE("stream err.\n"); return FALSE; } // VIDEO ストリームを開く if(avi.in_video) { avi.in_frame = NULL; AVIStreamInfo(avi.in_video,&avi.info,sizeof(AVISTREAMINFO)); // read format LONG len; AVIStreamFormatSize(avi.in_video,0,&len); avi.lpSrcFmt = (LPBITMAPINFOHEADER)GlobalAlloc(GPTR,len); AVIStreamReadFormat(avi.in_video,0,avi.lpSrcFmt,&len); // frame open avi.in_frame = AVIStreamGetFrameOpen(avi.in_video,NULL); if(avi.in_frame == NULL){ if(!codecOpen()){ MessageBox(NULL,"AVI未対応",0,0); return FALSE; } } else avi.codec = FALSE; } else return FALSE; //-------------------------------------------------- // 映像情報 //-------------------------------------------------- if(avi.codec) bih = avi.lpDecFmt; else bih = avi.lpSrcFmt; iWidth = bih->biWidth; iHeight = bih->biHeight; avi.dwLength = AVIStreamLength(avi.in_video); iSize = ((iWidth*bih->biBitCount+31)&~0x1f)*bih->biPlanes/8*iHeight; avi.biSize = bih->biSize + bih->biClrUsed*sizeof(RGBQUAD); avi.SPF = (int)(1000.*avi.info.dwScale/(double)avi.info.dwRate); //-------------------------------------------------- // ウィンドウ幅の設定 //-------------------------------------------------- set_window(bih); SetRect(&avi.dRgn,4,35,4+iWidth,35+iHeight); //-------------------------------------------------- // 画像領域の確保 //-------------------------------------------------- if(avi.codec==FALSE){ if(lpBMP) GlobalFree(lpBMP); lpBMP = (LPBYTE)GlobalAlloc(GPTR,avi.biSize+iSize); } AviReadFrame(0); // 1フレーム目の描画 InvalidateRect(hAppWnd,&avi.dRgn,FALSE); //-------------------------------------------------- // 再生位置,描画 の初期化 //-------------------------------------------------- avi.v_index = 0; aviLoad = TRUE; return TRUE; } //----------------------------------------------------------------- // コーデックによるデコードの開始 //----------------------------------------------------------------- int codecOpen() { LONG lFmtLength,lFrames,biLeng; //-------------------------------------------------- // get format size //-------------------------------------------------- AVIStreamFormatSize(avi.in_video,0,&lFmtLength); if(avi.lpDecFmt) GlobalFree(avi.lpDecFmt); avi.lpDecFmt = (LPBITMAPINFOHEADER)GlobalAlloc(GPTR,lFmtLength); //-------------------------------------------------- // read format //-------------------------------------------------- LPBITMAPINFOHEADER s,d; s = avi.lpSrcFmt; d = avi.lpDecFmt; AVIStreamInfo(avi.in_video,&avi.info,sizeof(AVISTREAMINFO)); lFrames = AVIStreamLength(avi.in_video); //-------------------------------------------------- // set decode format //-------------------------------------------------- memcpy(d,s,lFmtLength); d->biBitCount = 24; d->biCompression = BI_RGB; d->biSize = lFmtLength; biLeng = ((d->biWidth*d->biBitCount+31)&~0x1f)*d->biPlanes/8*d->biHeight; d->biSizeImage = biLeng; //-------------------------------------------------- // source size //-------------------------------------------------- biLeng = ((s->biWidth*s->biBitCount+31)&~0x1f)*s->biPlanes/8*s->biHeight; avi.lLength = biLeng; if(avi.info.dwSuggestedBufferSize) if(avi.info.dwSuggestedBufferSize < (unsigned)avi.lLength) avi.lLength = (LONG)avi.info.dwSuggestedBufferSize; // alloc size if(avi.lpSrc) GlobalFree(avi.lpSrc); if(lpBMP) GlobalFree(lpBMP); avi.lpSrc = (LPBYTE)GlobalAlloc(GPTR,s->biSizeImage); lpBMP = (LPBYTE)GlobalAlloc(GPTR,d->biSizeImage+lFmtLength); memcpy(lpBMP,d,lFmtLength); if(avi.lpSrc == NULL || lpBMP == NULL){ return FALSE; } //-------------------------------------------------- // open the decompressor //-------------------------------------------------- static char tag[4]="",buf[256]; memcpy(tag,&avi.info.fccHandler,sizeof(DWORD)); avi.hicd = ICDecompressOpen(ICTYPE_VIDEO,avi.info.fccHandler,s,d); if(!avi.hicd){ // cannot find codec sprintf(buf,"%s: cannot find codec",tag); MessageBox(NULL,buf,"AVI",0); return FALSE; } //-------------------------------------------------- // start decompressing //-------------------------------------------------- if(ICDecompressBegin(avi.hicd,avi.lpSrcFmt,avi.lpDecFmt)) return FALSE; avi.codec = TRUE; return TRUE; } //----------------------------------------------------------------- // フレームの取得 //----------------------------------------------------------------- void AviReadFrame(int vidx) { avi.v_index ++; if(avi.codec == FALSE){ avi.lpSrc = (LPBYTE)AVIStreamGetFrame(avi.in_frame,vidx); CopyMemory(lpBMP,avi.lpSrc,avi.biSize+iSize); } else { AVIStreamRead(avi.in_video,vidx,1,avi.lpSrc,avi.lLength,NULL,NULL); LPBYTE dst = lpBMP + avi.lpDecFmt->biSize; ICDecompress(avi.hicd,0,avi.lpSrcFmt,avi.lpSrc,avi.lpDecFmt,dst); CopyMemory(lpBMP,avi.lpDecFmt,avi.lpDecFmt->biSize); } } //-----------------------------------------------------r------------ // AVI を閉じる //----------------------------------------------------------------- void AviClose() { if(avi.codec){ if(avi.lpSrc) { GlobalFree(avi.lpSrc); avi.lpSrc = NULL; } if(avi.lpDecFmt) { GlobalFree(avi.lpDecFmt); avi.lpDecFmt = NULL; } ICDecompressEnd(avi.hicd); ICClose(avi.hicd); avi.codec = FALSE; } if(avi.in_frame) { AVIStreamGetFrameClose(avi.in_frame); avi.in_frame = NULL; } if(avi.in_video) { AVIStreamRelease(avi.in_video); avi.in_video = NULL; } if(avi.in_file) { AVIFileRelease(avi.in_file); avi.in_file = NULL; } if(avi.lpSrcFmt) { GlobalFree(avi.lpSrcFmt); avi.lpSrcFmt = NULL; } if(lpBMP) { GlobalFree(lpBMP); lpBMP = NULL; } AVIFileExit(); } //----------------------------------------------------------------- // コマ送り //----------------------------------------------------------------- void AviNext() { int vi,ret; TCHAR cbuf[256]; // 読取/表示 for(vi=avi.v_index;vi<avi.dwLength;vi++){ AviReadFrame(vi); // 再描画 InvalidateRect(hAppWnd,&avi.dRgn,FALSE); // タイトル wsprintf(cbuf,"[%s] frame %d/%d",avi.in_path,vi+1,avi.dwLength); SetWTitle(cbuf); ret = MessageBox(NULL, "Next:はい Prev:いいえ 停止:キャンセル","コマ送り", MB_YESNOCANCEL); if(ret==IDNO){ vi-=2; if(vi<-1) vi=0; } if(ret==IDCANCEL) break; } if(vi==avi.dwLength-1) avi.v_index = 0; } //----------------------------------------------------------------- // 連続再生 //----------------------------------------------------------------- void AviView() { if(vRun == FALSE){ // 描画用スレッドの開始 DWORD tid; vRun = TRUE; SetWindowText(hwB2,"停止"); CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)th_Proc, NULL,0,&tid); } else { SetWindowText(hwB2,"再生"); vRun = FALSE; } } //----------------------------------------------------------------- // AVI 連続再生用スレッド //----------------------------------------------------------------- DWORD th_Proc() { int vi; TCHAR cbuf[256]; for(vi=avi.v_index;vi<avi.dwLength;vi++){ if(vRun == FALSE) break; AviReadFrame(vi); // 再描画 InvalidateRect(hAppWnd,&avi.dRgn,FALSE); // タイトル wsprintf(cbuf,"%s -- frame %d/%d [%s]", avi.in_path,vi+1,avi.dwLength,getfps()); SetWTitle(cbuf); // 待機時間 Sleep(avi.SPF); } // 再生終了 if(vi==avi.dwLength) avi.v_index = 0; SetWindowText(hwB2,"再生"); vRun = FALSE; return 0; } |