#include "stdafx.h" #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <crtdbg.h> #include <TCHAR.h> #include <vfw.h> #include <mmreg.h> #include <msacm.h> #pragma comment (lib, "msacm32.lib") #pragma comment (lib, "winmm.lib") #pragma comment (lib, "vfw32.lib") // // ビデオ再生用データ // typedef struct _videothreaddata { HWND hWnd; BOOL bExit; PAVISTREAM lpVideoStream; } VIDEOTHREADDATA, *LPVIDEOTHREADDATA; // // Wave 再生用データ // typedef struct { LPBYTE lpData; WAVEHDR waveHdr; HWAVEOUT hwo; BOOL bActive; PAVISTREAM lpAudioStream; BOOL bPlaying; } WAVETHREADDATA, *LPWAVETHREADDATA; typedef struct _moviedata{ CRITICAL_SECTION m_csSema; PAVIFILE lpAviFile; HANDLE hVideoThread; BOOL bLoop; VIDEOTHREADDATA vtd; WAVETHREADDATA wtd; } MOVIEDATA, *LPMOVIEDATA; static DWORD WINAPI ThreadProc(LPVOID); void CALLBACK wavePlaybackProc(HWAVEOUT hwo, UINT uMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2); LRESULT CALLBACK WndProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam); HWND g_hWnd = NULL; static char * g_lpData = NULL; static long g_cfileSize; LRESULT CALLBACK IOProc(LPMMIOINFO lpMMIOInfo, UINT uMessage, LPARAM lParam1, LPARAM lParam2); // オフスクリーン描画用バッファ #define DRAW_OFFSCREEN #ifdef DRAW_OFFSCREEN HDC g_hOffscreen = NULL; #endif MOVIEDATA g_movieData; void StartPlayback(LPMOVIEDATA pMovieData, HWND hWnd){ DWORD dwSize; DWORD dwThreadId; PAVIFILE lpaf; WAVEFORMATEX wf; WAVETHREADDATA *lpwtd= &pMovieData->wtd; VIDEOTHREADDATA *lpvtd = &pMovieData->vtd; AVIFileInit(); if (AVIFileOpen(&pMovieData->lpAviFile, TEXT("test.MEV+"), OF_READ, NULL) != 0) return; if (AVIFileGetStream(pMovieData->lpAviFile, &lpvtd->lpVideoStream, streamtypeVIDEO, 0) != 0) lpvtd->lpVideoStream = NULL; if (AVIFileGetStream(pMovieData->lpAviFile, &lpwtd->lpAudioStream, streamtypeAUDIO, 0) != 0) lpwtd->lpAudioStream = NULL; AVIFileRelease(pMovieData->lpAviFile); if (lpwtd->lpAudioStream != NULL){ // オーディオ変換 { LONG lStart, lEnd; LONG lSize; LPBYTE lpAviData; LPWAVEFORMATEX lpwfAvi; DWORD numPCMSample; lStart = AVIStreamStart(lpwtd->lpAudioStream); lEnd = lStart + AVIStreamLength(lpwtd->lpAudioStream); AVISTREAMINFO asInfo; AVIStreamInfo(lpwtd->lpAudioStream, &asInfo, sizeof(AVISTREAMINFO)); AVIStreamReadFormat(lpwtd->lpAudioStream, lStart, NULL, &lSize); lpwfAvi = (LPWAVEFORMATEX)new BYTE[lSize]; AVIStreamReadFormat(lpwtd->lpAudioStream, lStart, lpwfAvi, &lSize); AVIStreamRead(lpwtd->lpAudioStream, lStart, lEnd, NULL, 0, &lSize, NULL); lpAviData = new BYTE[lSize]; AVIStreamRead(lpwtd->lpAudioStream, lStart, lEnd, lpAviData, lSize, NULL, NULL); MMRESULT mmr; HACMSTREAM has; ACMSTREAMHEADER ash; DWORD dwAviSize = lSize; wf.cbSize = sizeof(WAVEFORMATEX); wf.nSamplesPerSec = lpwfAvi->nSamplesPerSec; wf.wFormatTag = WAVE_FORMAT_PCM; if (lpwfAvi->wFormatTag == wf.wFormatTag){ wf = *lpwfAvi; }else acmFormatSuggest(NULL, lpwfAvi, &wf, sizeof(WAVEFORMATEX), ACM_FORMATSUGGESTF_WFORMATTAG|ACM_FORMATSUGGESTF_NSAMPLESPERSEC); mmr = acmStreamOpen(&has, NULL, lpwfAvi, &wf, NULL, 0, 0, ACM_STREAMOPENF_NONREALTIME); if (mmr != 0) { if (mmr == ACMERR_NOTPOSSIBLE) _RPT0(_CRT_WARN,TEXT("Error-acmStreamOpen\n")); return; } acmStreamSize(has, dwAviSize, &dwSize, ACM_STREAMSIZEF_SOURCE); lpwtd->lpData = new BYTE[dwSize]; ZeroMemory((BYTE*)&ash,sizeof(ACMSTREAMHEADER)); ash.cbStruct = sizeof(ACMSTREAMHEADER); ash.pbSrc = lpAviData; ash.cbSrcLength = dwAviSize; ash.pbDst = lpwtd->lpData; ash.cbDstLength = dwSize; acmStreamPrepareHeader(has, &ash, 0); acmStreamConvert(has, &ash, 0); acmStreamUnprepareHeader(has, &ash, 0); acmStreamClose(has, 0); delete lpwfAvi; delete lpAviData; } waveOutOpen(&lpwtd->hwo, WAVE_MAPPER, &wf, (DWORD)wavePlaybackProc, (DWORD)pMovieData, CALLBACK_FUNCTION); lpwtd->bPlaying = true; lpwtd->bActive = (lpwtd->lpAudioStream != NULL && lpwtd->hwo != NULL); lpwtd->waveHdr.lpData = (LPSTR)lpwtd->lpData; lpwtd->waveHdr.dwFlags = 0; lpwtd->waveHdr.dwBufferLength = dwSize; waveOutPrepareHeader(lpwtd->hwo, &lpwtd->waveHdr, sizeof(WAVEHDR)); waveOutWrite(lpwtd->hwo, &lpwtd->waveHdr, sizeof(WAVEHDR)); } if (lpvtd->lpVideoStream != NULL){ lpvtd->hWnd = hWnd; lpvtd->bExit = 0; pMovieData->hVideoThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ThreadProc, pMovieData, 0, &dwThreadId); } } void StopPlayback(LPMOVIEDATA pMovieData){ WAVETHREADDATA *lpwtd= &pMovieData->wtd; VIDEOTHREADDATA *lpvtd = &pMovieData->vtd; lpwtd->bActive = false; if (pMovieData->hVideoThread != NULL) { lpvtd->bExit = TRUE; WaitForSingleObject(pMovieData->hVideoThread, 1000); CloseHandle(pMovieData->hVideoThread); pMovieData->hVideoThread = NULL; } if (lpwtd->hwo != NULL) { waveOutReset(lpwtd->hwo); waveOutUnprepareHeader(lpwtd->hwo, &lpwtd->waveHdr, sizeof(WAVEHDR)); waveOutClose(lpwtd->hwo); } if (lpvtd->lpVideoStream != NULL) AVIStreamRelease(lpvtd->lpVideoStream); if (lpwtd->lpAudioStream != NULL) AVIStreamRelease(lpwtd->lpAudioStream); if (lpwtd->lpData != NULL){ delete lpwtd->lpData; lpwtd->lpData = NULL; } AVIFileExit(); } int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow ) { _CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF ); WNDCLASSEX wcex; wcex.cbSize = sizeof(WNDCLASSEX); wcex.style = CS_HREDRAW | CS_VREDRAW; wcex.lpfnWndProc = (WNDPROC)WndProc; wcex.cbClsExtra = 0; wcex.cbWndExtra = 0; wcex.hInstance = hInstance; wcex.hIcon = LoadIcon(NULL, MAKEINTRESOURCE(IDI_APPLICATION)); wcex.hCursor = LoadCursor(NULL, MAKEINTRESOURCE(IDC_ARROW)); wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); wcex.lpszMenuName = NULL; wcex.lpszClassName = _T("Game"); wcex.hIconSm = LoadIcon(NULL, IDI_APPLICATION); RegisterClassEx(&wcex); ZeroMemory((BYTE*)&g_movieData,sizeof(MOVIEDATA)); InitializeCriticalSection(&g_movieData.m_csSema); // ファイル読み込み struct _stat status; if (0 == _tstat(_T("test.avi"),&status)){ g_cfileSize = status.st_size; g_lpData = new char[g_cfileSize]; FILE *fp = fopen(_T("test.avi"),_T("rb")); if (fp != NULL){ fread(g_lpData,g_cfileSize,1,fp); fclose(fp); } // mci:カスタムIOProc のインストール mmioInstallIOProc(mmioFOURCC('M', 'E', 'V', ' '), (LPMMIOPROC)IOProc, MMIO_INSTALLPROC | MMIO_GLOBALPROC); } HWND hWnd; hWnd = CreateWindowEx(WS_EX_OVERLAPPEDWINDOW,wcex.lpszClassName,_T("Game"), WS_VISIBLE|WS_CAPTION|WS_SYSMENU|WS_MINIMIZEBOX|WS_MAXIMIZEBOX, CW_USEDEFAULT,0,640,480,NULL,NULL,hInstance,NULL); if( !hWnd ) return FALSE; RECT bounds,client; GetWindowRect(hWnd,&bounds); GetClientRect(hWnd,&client); MoveWindow(hWnd,bounds.left,bounds.top, 640 * 2 - client.right, 480 * 2 - client.bottom, false ); g_hWnd = hWnd; SetWindowLong(hWnd,GWL_USERDATA,(DWORD)&g_movieData); ShowWindow( hWnd, nCmdShow ); UpdateWindow( hWnd ); if (g_lpData == NULL){ DestroyWindow(hWnd); } #ifdef DRAW_OFFSCREEN HDC dc = GetDC(hWnd); HBITMAP hBitmap = CreateCompatibleBitmap(dc,640,480); g_hOffscreen = CreateCompatibleDC(dc); SelectObject(g_hOffscreen,hBitmap); ReleaseDC(hWnd,dc); #endif g_movieData.bLoop = TRUE; StartPlayback(&g_movieData, hWnd); MSG msg; while(true){ if(PeekMessage(&msg, 0, 0, 0, PM_REMOVE)){ if(msg.message == WM_QUIT) break; TranslateMessage(&msg); DispatchMessage(&msg); }else{ } } EnterCriticalSection(&g_movieData.m_csSema); if (g_lpData != NULL){ mmioInstallIOProc(mmioFOURCC('M', 'E', 'V', ' '), NULL,MMIO_REMOVEPROC); delete g_lpData; g_lpData = NULL; } LeaveCriticalSection(&g_movieData.m_csSema); #ifdef DRAW_OFFSCREEN DeleteDC(g_hOffscreen); DeleteObject(hBitmap); #endif DeleteCriticalSection(&g_movieData.m_csSema); return (int)msg.wParam; } // // 関数:WndProc // 説明:ウインドウに渡されたイベントのハンドラ // LRESULT CALLBACK WndProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam) { LPMOVIEDATA lpMovieData; lpMovieData = (LPMOVIEDATA)GetWindowLong(hWnd,GWL_USERDATA); switch (message) { case WM_CREATE: break; case WM_PAINT: #ifdef DRAW_OFFSCREEN if (g_hOffscreen != NULL){ HDC hDC; PAINTSTRUCT paintStruct; hDC = BeginPaint(hWnd,&paintStruct); EnterCriticalSection(&lpMovieData->m_csSema); BitBlt(hDC,0,0,640,480,g_hOffscreen,0,0,SRCCOPY); LeaveCriticalSection(&lpMovieData->m_csSema); ::EndPaint(hWnd,&paintStruct); } #endif return DefWindowProc(hWnd, message, wParam, lParam); case WM_DESTROY: lpMovieData->bLoop = FALSE; StopPlayback(lpMovieData); PostQuitMessage(0); return 0; default: break; } return DefWindowProc(hWnd, message, wParam, lParam); } void CALLBACK wavePlaybackProc( HWAVEOUT hwo, UINT uMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2 ){ MOVIEDATA *pMovieData = (MOVIEDATA*)dwInstance; WAVETHREADDATA *pwtd = &pMovieData->wtd; switch(uMsg){ case MM_WOM_DONE: pwtd->bPlaying = FALSE; break; case WOM_CLOSE: break; } } static DWORD WINAPI ThreadProc(LPVOID lp) { HDC hdc; LONG i,lCurrent; LONG lStart, lEnd; LONG lSize; HWND hWnd; LPBYTE lpBits; HDRAWDIB hdd; PAVISTREAM lpAviStream; LPMOVIEDATA lpMovieData = (LPMOVIEDATA)lp; LPVIDEOTHREADDATA lpvtd = (LPVIDEOTHREADDATA)&(lpMovieData->vtd); AVISTREAMINFO asi; BITMAPINFOHEADER bih; BOOL bOffscreen = false; lpAviStream = lpvtd->lpVideoStream; hWnd = lpvtd->hWnd; lStart = AVIStreamStart(lpAviStream); DWORD st = timeGetTime(); DWORD ct; lEnd = lStart + AVIStreamLength(lpAviStream); lCurrent = lEnd+1; AVIStreamInfo(lpAviStream, &asi, sizeof(AVISTREAMINFO)); hdd = DrawDibOpen(); int dxSrc, dySrc; lSize = sizeof(BITMAPINFOHEADER); AVIStreamReadFormat(lpAviStream, lStart, &bih, &lSize); dxSrc = bih.biWidth; dySrc = bih.biHeight; #ifdef DRAW_OFFSCREEN bOffscreen = DrawDibBegin(hdd,g_hOffscreen,dxSrc,dySrc,&bih,dxSrc,dySrc,DDF_BUFFER); #else hdc = GetDC(hWnd); bOffscreen = DrawDibBegin(hdd,hdc,dxSrc,dySrc,&bih,dxSrc,dySrc,DDF_BUFFER); ReleaseDC(hWnd,hdc); #endif while (!lpvtd->bExit) { while (true) { ct = timeGetTime() - st; i = AVIStreamTimeToSample(lpAviStream,(LONG)ct); if (i >= lEnd){ i = lEnd; lCurrent = lEnd; if (lpMovieData->bLoop == FALSE) break; LPWAVETHREADDATA lpwtd = &lpMovieData->wtd; if (!lpwtd->bActive){ st = timeGetTime(); }else{ // 音声再生中なら、終了を待つ if (!lpwtd->bPlaying){ st = timeGetTime(); waveOutReset(lpwtd->hwo); waveOutWrite(lpwtd->hwo, &lpwtd->waveHdr, sizeof(WAVEHDR)); } } } if (lCurrent != i){ #ifdef DRAW_OFFSCREEN hdc = g_hOffscreen; #else hdc = GetDC(hWnd); #endif if (i > (lCurrent + 1)){ _RPT0(_CRT_WARN,_T("Frame Dropped\n")); ++lCurrent; for (int j = 0; j < 4 ; ++j){ // up to x4 lSize = sizeof(BITMAPINFOHEADER); AVIStreamReadFormat(lpAviStream, lCurrent, &bih, &lSize); lpBits = new BYTE[ bih.biSizeImage ]; AVIStreamRead(lpAviStream, lCurrent, 1, lpBits, bih.biSizeImage, NULL, NULL); EnterCriticalSection(&lpMovieData->m_csSema); if (bOffscreen){ if (!DrawDibDraw(hdd, hdc, 0, 0, dxSrc, dySrc, &bih, lpBits, 0, 0, dxSrc, dySrc, DDF_DONTDRAW)) _RPT3(_CRT_WARN,_T("_DrawDibDraw failed. frame %d,%d,%d\n"),lCurrent,dxSrc,dySrc); else _RPT3(_CRT_WARN,_T("_DrawDibDraw succeeded. frame %d,%d,%d\n"),lCurrent,dxSrc,dySrc); }else if (!DrawDibDraw(hdd, hdc, 0, 0, dxSrc, dySrc, &bih, lpBits, 0, 0, dxSrc, dySrc, 0)){ _RPT1(_CRT_WARN,_T("_DrawDibDraw failed. frame %d\n"),lCurrent); } LeaveCriticalSection(&lpMovieData->m_csSema); delete lpBits; if (i <= ++lCurrent) break; } }else{ lCurrent = i; } lSize = sizeof(BITMAPINFOHEADER); AVIStreamReadFormat(lpAviStream, lCurrent, &bih, &lSize); lpBits = new BYTE[bih.biSizeImage]; AVIStreamRead(lpAviStream, lCurrent, 1, lpBits, bih.biSizeImage, NULL, NULL); #if 1 EnterCriticalSection(&lpMovieData->m_csSema); if (bOffscreen){ if (!DrawDibDraw(hdd, hdc, 0, 0, dxSrc, dySrc, &bih, lpBits, 0, 0, dxSrc, dySrc, DDF_DONTDRAW)) _RPT3(_CRT_WARN,_T("DrawDibDraw failed. frame %d,%d,%d\n"),lCurrent,dxSrc,dySrc); else{ DrawDibDraw(hdd, hdc, 0, 0, dxSrc, dySrc, &bih, lpBits, 0, 0, dxSrc, dySrc, DDF_UPDATE); _RPT3(_CRT_WARN,_T("DrawDibDraw succeeded. frame %d,%d,%d\n"),lCurrent,dxSrc,dySrc); } }else if (!DrawDibDraw(hdd, hdc, 0, 0, dxSrc, dySrc, &bih, lpBits, 0, 0, dxSrc, dySrc, 0)){ _RPT1(_CRT_WARN,_T("DrawDibDraw failed. frame %d\n"),lCurrent); } LeaveCriticalSection(&lpMovieData->m_csSema); #else _RPT1(_CRT_WARN,_T("DrawDibDraw failed. frame %d\n"),lCurrent); #endif delete lpBits; #ifdef DRAW_OFFSCREEN InvalidateRect(hWnd,NULL,false); #else ReleaseDC(hWnd, hdc); #endif } if (lpvtd->bExit) break; Yield(); } } DrawDibClose(hdd); return 0; } // // mmio: IOProc // LRESULT CALLBACK IOProc(LPMMIOINFO lpMMIOInfo, UINT uMessage, LPARAM lParam1, LPARAM lParam2) { static BOOL alreadyOpened = FALSE; switch (uMessage) { case MMIOM_OPEN: if (alreadyOpened) return 0; alreadyOpened = TRUE; lpMMIOInfo->lDiskOffset = 0; return 0; case MMIOM_CLOSE: return 0; case MMIOM_READ:{ LPARAM dataRead = 0; EnterCriticalSection(&g_movieData.m_csSema); if (g_lpData != NULL){ dataRead = lParam2; memcpy((void *)lParam1, g_lpData+lpMMIOInfo->lDiskOffset, lParam2); lpMMIOInfo->lDiskOffset += lParam2; dataRead = lParam2; } LeaveCriticalSection(&g_movieData.m_csSema); return (dataRead); } case MMIOM_SEEK: switch (lParam2) { case SEEK_SET: lpMMIOInfo->lDiskOffset = lParam1; break; case SEEK_CUR: lpMMIOInfo->lDiskOffset += lParam1; break; case SEEK_END: lpMMIOInfo->lDiskOffset = g_cfileSize - lParam1; break; } return lpMMIOInfo->lDiskOffset; default: return -1; } }