001: //-----------------------------------------------------------------
002: // aviplay.cpp:
003: //      AVI 再生/表示ウィンドウ
004: //                Last Update: <2005/01/28 11:21:26 A.Murakami>
005: //-----------------------------------------------------------------
006: #include <stdio.h>
007: #include <math.h>
008: #include <windows.h>
009: #include <vfw.h>
010: #include "aviplay.h"
011: //-----------------------------------------------------------------
012: #pragma comment(linker,"/subsystem:windows")
013: #pragma comment(lib,"winmm")
014: #pragma comment(lib,"vfw32")
015: //-----------------------------------------------------------------
016: // グローバル変数定義
017: //-----------------------------------------------------------------
018: HINSTANCE hAppInst; HWND hAppWnd;
019: UINT aviLoad,iWidth,iHeight,iSize;
020: LPBYTE lpBMP = NULL;
021: AviInfo avi;
022: double wm_xscale,wm_yscale;
023: static HWND hwB0,hwB1,hwB2,hwB3;
024: //-----------------------------------------------------------------
025: UINT vRun=FALSE; // 連続再生用
026: //-----------------------------------------------------------------
027: void TRACE(char *fmt,...)
028: {
029:     char szBuff[BUFSIZ];
030:     va_list ap;
031:     va_start(ap,fmt);
032:     vsprintf(szBuff,fmt,ap);
033: #ifdef _MSC_VER
034:     OutputDebugString(szBuff);
035: #else
036:     fprintf(stderr,szBuff);
037: #endif
038:     va_end(ap);
039: }
040: //-----------------------------------------------------------------
041: int WINAPI WinMain(HINSTANCE hCurInst, HINSTANCE hPrevInst,
042:                    LPSTR lpsCmdLine, int nCmdShow)
043: {
044:     MSG msg;
045:     LPCTSTR szClassName = "aviplay"; // 初期化
046:     aviLoad = FALSE; iWidth = 300; iHeight = 200;
047:     // ウィンドウ・クラスの登録
048:     if(!hPrevInst){
049:         if(!InitApp(hCurInst,szClassName)) return 0;
050:         hAppInst = hCurInst;
051:     }
052:     // ウィンドウ作成
053:     if(!InitInstance(hCurInst,szClassName,lpsCmdLine,nCmdShow)){
054:         return 0;
055:     }
056:     // AVI 初期化
057:     AviInit();
058:     // メッセージループ
059:     while(GetMessage(&msg,NULL,0,0)){
060:         TranslateMessage(&msg);
061:         DispatchMessage(&msg);
062:     }
063:     return msg.wParam;
064: }
065: //-----------------------------------------------------------------
066: // ウィンドウ・クラスの登録
067: //-----------------------------------------------------------------
068: BOOL InitApp(HINSTANCE hInst, LPCSTR szClassName)
069: {
070:     WNDCLASSEX wc;
071:     wc.cbSize        = sizeof(wc);
072:     wc.style         = CS_HREDRAW | CS_VREDRAW;
073:     wc.lpfnWndProc   = WndProc; // プロシージャ名
074:     wc.cbClsExtra    = 0;
075:     wc.cbWndExtra    = 0;
076:     wc.hInstance     = hInst;   // インスタンス
077:     wc.hIcon         = LoadIcon(NULL,IDI_APPLICATION);
078:     wc.hCursor       = LoadCursor(NULL,IDC_ARROW);
079:     wc.hIconSm       = LoadIcon(NULL,IDI_APPLICATION);
080:     wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
081:     wc.lpszMenuName  = NULL;    // メニュー名
082:     wc.lpszClassName = (LPCSTR)szClassName;
083:     return (RegisterClassEx(&wc));
084: }
085: //-----------------------------------------------------------------
086: // ウィンドウの生成
087: //-----------------------------------------------------------------
088: BOOL InitInstance(HINSTANCE hInst,LPCSTR szClassName,
089:                   LPSTR lpsCmdLine,int nCmdShow)
090: {
091:     TCHAR lpStr[256]; HWND hWnd;
092:     // ウィンドウの作成 
093:     wsprintf(lpStr,"動画ファイル(AVI)再生");
094:     hWnd = CreateWindow(szClassName,lpStr,   // タイトルバー
095:                         WS_OVERLAPPEDWINDOW, // ウィンドウ
096:                         CW_USEDEFAULT,       // X座標
097:                         CW_USEDEFAULT,       // Y座標
098:                         15+iWidth,           // 幅
099:                         65+iHeight,          // 高さ
100:                         NULL,                // 親ウィンドウのハンドル
101:                         NULL,                // メニューハンドル 
102:                         hInst,               // インスタンスハンドル
103:                         NULL);
104:     if(!hWnd) return FALSE;
105:     ShowWindow(hWnd,nCmdShow);
106:     UpdateWindow(hWnd);
107:     hAppWnd = hWnd;
108:     return TRUE;
109: }
110: //-----------------------------------------------------------------
111: // メッセージ情報
112: //-----------------------------------------------------------------
113: LRESULT CALLBACK WndProc(HWND hwnd,UINT iMsg,WPARAM wParam,LPARAM lParam)
114: {
115:     switch(iMsg){
116:     case WM_CHAR:
117:         if(wParam=='q') SendMessage(hwnd,WM_DESTROY,0,0); break;
118:     case WM_CREATE: {
119:         // ボタン作成
120:         hwB0 = CreateWindow("Button","開く",WS_CHILD | WS_VISIBLE,
121:                             5,5,55,25,hwnd,(HMENU)ID_OPEN,hAppInst,NULL);
122:         hwB1 = CreateWindow("Button","コマ送り",WS_CHILD | WS_VISIBLE,
123:                             60,5,70,25,hwnd,(HMENU)ID_AVI1,hAppInst,NULL);
124:         hwB2 = CreateWindow("Button","再生",WS_CHILD | WS_VISIBLE,
125:                             130,5,55,25,hwnd,(HMENU)ID_AVI2,hAppInst,NULL);
126:         hwB3 = CreateWindow("Button","保存",WS_CHILD | WS_VISIBLE,
127:                             185,5,55,25,hwnd,(HMENU)ID_SAVE,hAppInst,NULL);
128:     } break;
129:     case WM_COMMAND:
130:         switch(LOWORD(wParam)){
131:         case 'q':     // 終了
132:             SendMessage(hwnd,WM_DESTROY,0,0); break;
133:         case ID_OPEN: // ファイル選択
134:             if(vRun) break;
135:             if(OpenFile(avi.in_path)){ AviOpen(); }
136:             break;
137:         case ID_AVI1: // コマ送り
138:             if(aviLoad) AviNext();
139:             break;
140:         case ID_AVI2: // 連続再生
141:             if(aviLoad) AviView();
142:             break;
143:         case ID_SAVE: // 画像保存
144:             static TCHAR szFn[MAX_PATH];
145:             if(SaveFile(szFn)){ SaveBMP(szFn,lpBMP); }
146:             break;
147:         }
148:         break;
149:     case WM_PAINT:
150:         if(aviLoad){
151:             HDC hdc; PAINTSTRUCT ps;
152:             // DIBを画面に描画
153:             hdc = BeginPaint(hwnd,&ps);
154:             StretchDIBits(hdc,4,35,
155:                           (int)(wm_xscale*iWidth),(int)(wm_yscale*iHeight),
156:                           0,0,iWidth,iHeight,
157:                           lpBMP+avi.biSize,(LPBITMAPINFO)lpBMP,
158:                           DIB_RGB_COLORS,SRCCOPY);
159:             EndPaint(hwnd,&ps);
160:         }
161:         break;
162:     case WM_SIZE: { // ウィンドウ幅の変更
163:         UINT wx,wy;
164:         WINDOWPLACEMENT wndpl;
165:         GetWindowPlacement(hAppWnd,&wndpl);
166:         wx = wndpl.rcNormalPosition.right - wndpl.rcNormalPosition.left;
167:         wy = wndpl.rcNormalPosition.bottom - wndpl.rcNormalPosition.top;
168:         wx -= 14; wy -= 84;
169:         wm_xscale = wx/(double)iWidth;
170:         wm_yscale = wy/(double)iHeight;
171:         // 再描画範囲
172:         SetRect(&avi.dRgn,
173:                 4,35,4+(int)(iWidth*wm_xscale),35+(int)(iHeight*wm_yscale));
174:     } break;
175:     case WM_DESTROY: // メモリの解放
176:         if(aviLoad) AviClose();
177:         PostQuitMessage(0);
178:         break;
179:     }
180:     return DefWindowProc(hwnd,iMsg,wParam,lParam);
181: }
182: //-----------------------------------------------------------------
183: // ウィンドウタイトルの設定
184: //-----------------------------------------------------------------
185: void SetWTitle(LPTSTR tstr)
186: {
187:     SetWindowText(hAppWnd,tstr);
188: }
189: char *getfps(void)
190: {
191:     static DWORD last     = 0;
192:     static DWORD frames   = 0;
193:     static char  buf[256] = "";
194:     DWORD  current = timeGetTime();
195:     frames++;
196:     if(500 <= current - last) { // 0.5秒毎に更新
197:         double dt  = (double)(current - last) / 1000.0f;
198:         double fps = (double)frames / dt;
199:         last = current;
200:         frames = 0;
201:         sprintf(buf,"%.02f fps",fps);
202:     }
203:     return buf;
204: }
205: //-----------------------------------------------------------------
206: // 画像情報の設定
207: //-----------------------------------------------------------------
208: void set_window(LPBITMAPINFOHEADER bih)
209: {
210:     TCHAR tStr[256];
211:     // 画像情報
212:     iWidth  = bih->biWidth;
213:     iHeight = bih->biHeight;
214:     // ウィンドウ幅の変更
215:     UINT wx,wy;
216:     WINDOWPLACEMENT wndpl;
217:     GetWindowPlacement(hAppWnd,&wndpl);
218:     wx = wndpl.rcNormalPosition.left;
219:     wy = wndpl.rcNormalPosition.top;
220:     MoveWindow(hAppWnd,wx,wy,15+iWidth,65+iHeight,TRUE);
221:     // キャプション
222:     wsprintf(tStr,"[%s] frame %d/%d",avi.in_path,1,avi.dwLength);
223:     SetWTitle(tStr);
224:     // 描画サイズ
225:     wm_xscale = wm_yscale = 1.0;
226: }
227: //-----------------------------------------------------------------
228: // [開く]ファイルの選択
229: //-----------------------------------------------------------------
230: UINT OpenFile(LPTSTR lpFname)
231: {
232:     static OPENFILENAME ofn;
233:     TCHAR szFt[MAX_PATH];
234:     lpFname[0] = NULL;
235:     // ファイル[開く]ダイアログ設定
236:     ofn.lStructSize       = sizeof(OPENFILENAME);
237:     ofn.hwndOwner         = hAppWnd;
238:     ofn.hInstance         = NULL;
239:     ofn.lpstrFilter       =
240:         "AVI stream(*.avi)\0*.avi\0"
241:         "すべてのファイル(*.*)\0*.*\0\0";
242:     ofn.lpstrCustomFilter = NULL;
243:     ofn.nMaxCustFilter    = 0;
244:     ofn.nFilterIndex      = 1;       // フィルタの初期位置
245:     ofn.lpstrFile         = lpFname; // ファイル名用文字列バッファ
246:     ofn.nMaxFile          = 255;     // 文字列バッファのサイズ
247:     ofn.lpstrFileTitle    = szFt;    // タイトル用文字列バッファ
248:     ofn.nMaxFileTitle     = 255;     // 文字列バッファのサイズ
249:     ofn.lpstrInitialDir   = NULL;
250:     ofn.lpstrTitle        = "動画ファイル(AVI)の選択";
251:     ofn.Flags             = OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;
252:     ofn.nFileOffset       = 0;
253:     ofn.nFileExtension    = 0;
254:     ofn.lpstrDefExt       = "avi";
255:     ofn.lCustData         = 0;
256:     ofn.lpfnHook          = NULL;
257:     ofn.lpTemplateName    = NULL;
258:     // ダイアログを開く
259:     if(!GetOpenFileName(&ofn)) return FALSE;
260:     return TRUE;
261: }
262: //-----------------------------------------------------------------
263: // [保存]ファイルの選択
264: //-----------------------------------------------------------------
265: UINT SaveFile(LPTSTR lpFname)
266: {
267:     static OPENFILENAME ofn;
268:     TCHAR szFt[MAX_PATH];
269:     lpFname[0] = NULL;
270:     // ファイル[保存]ダイアログ設定
271:     ofn.lStructSize       = sizeof(OPENFILENAME);
272:     ofn.hwndOwner         = hAppWnd;
273:     ofn.hInstance         = NULL;
274:     ofn.lpstrFilter       =
275:         "Bitmap(*.bmp)\0*.bmp\0"
276:         "すべてのファイル(*.*)\0*.*\0\0";
277:     ofn.lpstrCustomFilter = NULL;
278:     ofn.nMaxCustFilter    = 0;
279:     ofn.nFilterIndex      = 1;       // フィルタの初期位置
280:     ofn.lpstrFile         = lpFname; // ファイル名用文字列バッファ
281:     ofn.nMaxFile          = 255;     // 文字列バッファのサイズ
282:     ofn.lpstrFileTitle    = szFt;
283:     ofn.nMaxFileTitle     = 255;     // 文字列バッファのサイズ
284:     ofn.lpstrInitialDir   = NULL;
285:     ofn.lpstrTitle        = "画像(BMP)の保存";
286:     ofn.Flags             = OFN_OVERWRITEPROMPT | OFN_HIDEREADONLY;
287:     ofn.nFileOffset       = 0;
288:     ofn.nFileExtension    = 0;
289:     ofn.lpstrDefExt       = "bmp";
290:     ofn.lCustData         = 0;
291:     ofn.lpfnHook          = NULL;
292:     ofn.lpTemplateName    = NULL;
293:     // ダイアログを開く
294:     if(!GetSaveFileName(&ofn)) return FALSE;
295:     return TRUE;
296: }
297: //-----------------------------------------------------------------
298: // ビットマップの保存
299: //-----------------------------------------------------------------
300: UINT SaveBMP(LPCTSTR lpFname,LPBYTE lpBuf)
301: {
302:     HANDLE hdlBmp;
303:     BITMAPFILEHEADER bfhBmp;
304:     LPBITMAPINFOHEADER bih = (LPBITMAPINFOHEADER)lpBuf;
305:     LONG offset = sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER);
306:     LONG cpsize = bih->biClrUsed*sizeof(RGBQUAD);
307:     UINT bCnt = bih->biBitCount,bPln = bih->biPlanes;
308:     DWORD dwdPixelsSize,wSize;
309:     // 画像サイズ
310:     dwdPixelsSize = ((bih->biWidth*bCnt+31)&~0x1f)*bPln/8;
311:     dwdPixelsSize *= bih->biHeight;
312:     // ファイルを開く
313:     hdlBmp = CreateFile(lpFname,GENERIC_WRITE,0,NULL,OPEN_ALWAYS,
314:                         FILE_ATTRIBUTE_NORMAL,NULL);
315:     if(hdlBmp==INVALID_HANDLE_VALUE){
316:         MessageBox(NULL,"書込みに失敗しました",NULL,MB_OK);
317:         return FALSE;
318:     }
319:     // ヘッダ情報
320:     bfhBmp.bfType      = 'M'*256+'B';
321:     bfhBmp.bfSize      = offset + cpsize + dwdPixelsSize;
322:     bfhBmp.bfReserved1 = 0;
323:     bfhBmp.bfReserved2 = 0;
324:     bfhBmp.bfOffBits   = offset + cpsize;
325:     // 書込み
326:     dwdPixelsSize += sizeof(BITMAPINFOHEADER) + cpsize;
327:     WriteFile(hdlBmp,&bfhBmp,sizeof(BITMAPFILEHEADER),&wSize,NULL);
328:     WriteFile(hdlBmp,lpBuf,sizeof(BYTE)*dwdPixelsSize,&wSize,NULL);
329:     // 後片付け
330:     CloseHandle(hdlBmp);
331:     return TRUE;
332: }
333: //-----------------------------------------------------------------
334: // AVI 初期化
335: //-----------------------------------------------------------------
336: void AviInit()
337: {
338:     avi.in_file  = NULL;
339:     avi.in_video = NULL;
340:     avi.in_frame = NULL;
341:     avi.lpSrcFmt = NULL;
342:     avi.lpDecFmt = NULL;
343:     avi.lpSrc    = NULL;
344:     lpBMP        = NULL;
345:     avi.codec    = FALSE;
346: }
347: //-----------------------------------------------------------------
348: // AVIのオープン
349: //-----------------------------------------------------------------
350: int AviOpen()
351: {
352:     AVIFILEINFO pfi;
353:     LPBITMAPINFOHEADER bih;
354:     //--------------------------------------------------
355:     // Video for Windows(VFW) によるAVIのオープン
356:     //--------------------------------------------------
357:     if(aviLoad) AviClose();
358:     AVIFileInit();
359:     if(AVIFileOpen(&avi.in_file,avi.in_path,OF_READ,NULL)){
360:         TRACE("open err.\n");
361:         return FALSE;
362:     }
363:     //--------------------------------------------------
364:     // 映像情報
365:     //--------------------------------------------------
366:     AVIFileInfo(avi.in_file,&pfi,sizeof(AVIFILEINFO));
367:     if(AVIFileGetStream(avi.in_file,&avi.in_video,streamtypeVIDEO,0)){
368:         TRACE("stream err.\n");
369:         return FALSE;
370:     }
371:     // VIDEO ストリームを開く
372:     if(avi.in_video) {
373:         avi.in_frame = NULL;
374:         AVIStreamInfo(avi.in_video,&avi.info,sizeof(AVISTREAMINFO));
375:         // read format
376:         LONG len;
377:         AVIStreamFormatSize(avi.in_video,0,&len);
378:         avi.lpSrcFmt = (LPBITMAPINFOHEADER)GlobalAlloc(GPTR,len);
379:         AVIStreamReadFormat(avi.in_video,0,avi.lpSrcFmt,&len);
380:         // frame open
381:         avi.in_frame = AVIStreamGetFrameOpen(avi.in_video,NULL);
382:         if(avi.in_frame == NULL){
383:             if(!codecOpen()){
384:                 MessageBox(NULL,"AVI未対応",0,0);
385:                 return FALSE;
386:             }
387:         } else avi.codec = FALSE;
388:     } else return FALSE;
389:     //--------------------------------------------------
390:     // 映像情報
391:     //--------------------------------------------------
392:     if(avi.codec) bih = avi.lpDecFmt;
393:     else          bih = avi.lpSrcFmt;
394:     iWidth  = bih->biWidth;
395:     iHeight = bih->biHeight;
396:     avi.dwLength = AVIStreamLength(avi.in_video);
397:     iSize = ((iWidth*bih->biBitCount+31)&~0x1f)*bih->biPlanes/8*iHeight;
398:     avi.biSize = bih->biSize + bih->biClrUsed*sizeof(RGBQUAD);
399:     avi.SPF = (int)(1000.*avi.info.dwScale/(double)avi.info.dwRate);
400:     //--------------------------------------------------
401:     // ウィンドウ幅の設定
402:     //--------------------------------------------------
403:     set_window(bih);
404:     SetRect(&avi.dRgn,4,35,4+iWidth,35+iHeight);
405:     //--------------------------------------------------
406:     // 画像領域の確保
407:     //--------------------------------------------------
408:     if(avi.codec==FALSE){
409:         if(lpBMP) GlobalFree(lpBMP);
410:         lpBMP = (LPBYTE)GlobalAlloc(GPTR,avi.biSize+iSize);
411:     }
412:     AviReadFrame(0);
413:     // 1フレーム目の描画
414:     InvalidateRect(hAppWnd,&avi.dRgn,FALSE);
415:     //--------------------------------------------------
416:     // 再生位置,描画 の初期化
417:     //--------------------------------------------------
418:     avi.v_index = 0;
419:     aviLoad = TRUE;
420:     return TRUE;
421: }
422: //-----------------------------------------------------------------
423: // コーデックによるデコードの開始
424: //-----------------------------------------------------------------
425: int codecOpen()
426: {
427:     LONG lFmtLength,lFrames,biLeng;
428:     //--------------------------------------------------
429:     // get format size
430:     //--------------------------------------------------
431:     AVIStreamFormatSize(avi.in_video,0,&lFmtLength);
432:     if(avi.lpDecFmt) GlobalFree(avi.lpDecFmt);
433:     avi.lpDecFmt = (LPBITMAPINFOHEADER)GlobalAlloc(GPTR,lFmtLength);
434:     //--------------------------------------------------
435:     // read format
436:     //--------------------------------------------------
437:     LPBITMAPINFOHEADER s,d;
438:     s = avi.lpSrcFmt; d = avi.lpDecFmt;
439:     AVIStreamInfo(avi.in_video,&avi.info,sizeof(AVISTREAMINFO));
440:     lFrames = AVIStreamLength(avi.in_video);
441:     //--------------------------------------------------
442:     // set decode format
443:     //--------------------------------------------------
444:     memcpy(d,s,lFmtLength);
445:     d->biBitCount    = 24;
446:     d->biCompression = BI_RGB;
447:     d->biSize        = lFmtLength;
448:     biLeng = ((d->biWidth*d->biBitCount+31)&~0x1f)*d->biPlanes/8*d->biHeight;
449:     d->biSizeImage   = biLeng;
450:     //--------------------------------------------------
451:     // source size
452:     //--------------------------------------------------
453:     biLeng = ((s->biWidth*s->biBitCount+31)&~0x1f)*s->biPlanes/8*s->biHeight;
454:     avi.lLength    = biLeng;
455:     if(avi.info.dwSuggestedBufferSize)
456:         if(avi.info.dwSuggestedBufferSize < (unsigned)avi.lLength)
457:             avi.lLength = (LONG)avi.info.dwSuggestedBufferSize;
458:     // alloc size
459:     if(avi.lpSrc) GlobalFree(avi.lpSrc);
460:     if(lpBMP)     GlobalFree(lpBMP);
461:     avi.lpSrc = (LPBYTE)GlobalAlloc(GPTR,s->biSizeImage);
462:     lpBMP     = (LPBYTE)GlobalAlloc(GPTR,d->biSizeImage+lFmtLength);
463:     memcpy(lpBMP,d,lFmtLength);
464:     if(avi.lpSrc == NULL || lpBMP == NULL){ return FALSE; }
465:     //--------------------------------------------------
466:     // open the decompressor
467:     //--------------------------------------------------
468:     static char tag[4]="",buf[256];
469:     memcpy(tag,&avi.info.fccHandler,sizeof(DWORD));
470:     avi.hicd = ICDecompressOpen(ICTYPE_VIDEO,avi.info.fccHandler,s,d);
471:     if(!avi.hicd){ // cannot find codec
472:         sprintf(buf,"%s: cannot find codec",tag);
473:         MessageBox(NULL,buf,"AVI",0);
474:         return FALSE;
475:     }
476:     //--------------------------------------------------
477:     // start decompressing
478:     //--------------------------------------------------
479:     if(ICDecompressBegin(avi.hicd,avi.lpSrcFmt,avi.lpDecFmt))
480:         return FALSE;
481:     avi.codec = TRUE;
482:     return TRUE;
483: }
484: //-----------------------------------------------------------------
485: // フレームの取得
486: //-----------------------------------------------------------------
487: void AviReadFrame(int vidx)
488: {
489:     avi.v_index ++;
490:     if(avi.codec == FALSE){
491:         avi.lpSrc = (LPBYTE)AVIStreamGetFrame(avi.in_frame,vidx);
492:         CopyMemory(lpBMP,avi.lpSrc,avi.biSize+iSize);
493:     } else {
494:         AVIStreamRead(avi.in_video,vidx,1,avi.lpSrc,avi.lLength,NULL,NULL);
495:         LPBYTE dst = lpBMP + avi.lpDecFmt->biSize;
496:         ICDecompress(avi.hicd,0,avi.lpSrcFmt,avi.lpSrc,avi.lpDecFmt,dst);
497:         CopyMemory(lpBMP,avi.lpDecFmt,avi.lpDecFmt->biSize);
498:     }
499: }
500: //-----------------------------------------------------r------------
501: // AVI を閉じる
502: //-----------------------------------------------------------------
503: void AviClose()
504: {
505:     if(avi.codec){
506:         if(avi.lpSrc) { GlobalFree(avi.lpSrc); avi.lpSrc = NULL; }
507:         if(avi.lpDecFmt) { GlobalFree(avi.lpDecFmt); avi.lpDecFmt = NULL; }
508:         ICDecompressEnd(avi.hicd);
509:         ICClose(avi.hicd);
510:         avi.codec = FALSE;
511:     }
512:     if(avi.in_frame) {
513:         AVIStreamGetFrameClose(avi.in_frame); avi.in_frame = NULL;
514:     }
515:     if(avi.in_video) { AVIStreamRelease(avi.in_video); avi.in_video = NULL; }
516:     if(avi.in_file)  { AVIFileRelease(avi.in_file); avi.in_file = NULL; }
517:     if(avi.lpSrcFmt) { GlobalFree(avi.lpSrcFmt); avi.lpSrcFmt = NULL; }
518:     if(lpBMP)        { GlobalFree(lpBMP); lpBMP = NULL; }
519:     AVIFileExit();
520: }
521: //-----------------------------------------------------------------
522: // コマ送り
523: //-----------------------------------------------------------------
524: void AviNext()
525: {
526:     int vi,ret;
527:     TCHAR cbuf[256];
528:     // 読取/表示
529:     for(vi=avi.v_index;vi<avi.dwLength;vi++){
530:         AviReadFrame(vi);
531:         // 再描画
532:         InvalidateRect(hAppWnd,&avi.dRgn,FALSE);
533:         // タイトル
534:         wsprintf(cbuf,"[%s] frame %d/%d",avi.in_path,vi+1,avi.dwLength);
535:         SetWTitle(cbuf);
536:         ret = MessageBox(NULL, 
537:                          "Next:はい Prev:いいえ 停止:キャンセル","コマ送り",
538:                          MB_YESNOCANCEL);
539:         if(ret==IDNO){ vi-=2; if(vi<-1) vi=0; }
540:         if(ret==IDCANCEL) break;
541:     }
542:     if(vi==avi.dwLength-1) avi.v_index = 0;
543: }
544: //-----------------------------------------------------------------
545: // 連続再生
546: //-----------------------------------------------------------------
547: void AviView()
548: {
549:     if(vRun == FALSE){
550:         // 描画用スレッドの開始
551:         DWORD tid;
552:         vRun = TRUE;
553:         SetWindowText(hwB2,"停止");
554:         CreateThread(NULL, 0,
555:                      (LPTHREAD_START_ROUTINE)th_Proc,
556:                      NULL,0,&tid);
557:     } else {
558:         SetWindowText(hwB2,"再生");
559:         vRun = FALSE;
560:     }
561: }
562: //-----------------------------------------------------------------
563: // AVI 連続再生用スレッド
564: //-----------------------------------------------------------------
565: DWORD th_Proc()
566: {
567:     int vi; TCHAR cbuf[256];
568:     for(vi=avi.v_index;vi<avi.dwLength;vi++){
569:         if(vRun == FALSE) break;
570:         AviReadFrame(vi);
571:         // 再描画
572:         InvalidateRect(hAppWnd,&avi.dRgn,FALSE);
573:         // タイトル
574:         wsprintf(cbuf,"%s -- frame %d/%d [%s]",
575:                  avi.in_path,vi+1,avi.dwLength,getfps());
576:         SetWTitle(cbuf);
577:         // 待機時間
578:         Sleep(avi.SPF);
579:     }
580:     // 再生終了
581:     if(vi==avi.dwLength) avi.v_index = 0;
582:     SetWindowText(hwB2,"再生");
583:     vRun = FALSE;
584:     return 0;
585: }