AVIVIEW.C
上传用户:bangxh
上传日期:2007-01-31
资源大小:42235k
文件大小:65k
源码类别:

Windows编程

开发平台:

Visual C++

  1. /**************************************************************************
  2.  *
  3.  *  THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
  4.  *  KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
  5.  *  IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
  6.  *  PURPOSE.
  7.  *
  8.  *  Copyright (C) 1993 - 1997  Microsoft Corporation.  All Rights Reserved.
  9.  *
  10.  **************************************************************************/
  11. #define INC_OLE2
  12. #include <windows.h>
  13. #include <windowsx.h>
  14. #include <mmsystem.h>
  15. #include <memory.h>
  16. #include <commdlg.h>
  17. #include <vfw.h>
  18. #include "muldiv32.h"
  19. #include <mmreg.h>
  20. #include <msacm.h>
  21. #include "aviview.h"
  22. #include "audplay.h"
  23. #include "aviball.h"
  24. #define GlobalSizePtr(lp)   GlobalSize(GlobalPtrHandle(lp))
  25. #define FIXCC(fcc)  if (fcc == 0)       fcc = mmioFOURCC('N', 'o', 'n', 'e'); 
  26.                     if (fcc == BI_RLE8) fcc = mmioFOURCC('R', 'l', 'e', '8');
  27. /*----------------------------------------------------------------------------*
  28. *----------------------------------------------------------------------------*/
  29. typedef LONG (PASCAL *LPWNDPROC)(HWND, UINT, WPARAM, LPARAM); // pointer to a window procedure
  30. /*----------------------------------------------------------------------------*
  31. *----------------------------------------------------------------------------*/
  32. static  TCHAR   gszAppName[]=TEXT("AVIView");
  33. static  TCHAR gachFilter[512] = TEXT("");
  34. static  HANDLE ghInstApp;
  35. static  HWND ghwndApp;
  36. static  HACCEL ghAccel;
  37. static  HANDLE  ghLib;                 // Handle to palmap32.dll
  38. static  BOOL    gbCanPalMap = FALSE;   // Is palmap32.dll available?
  39. #define SCROLLRANGE  10000
  40. #define MAXNUMSTREAMS   50
  41. PAVIFILE     gpfile; // the current file
  42. PAVISTREAM          gapavi[MAXNUMSTREAMS]; // the current streams
  43. PGETFRAME     gapgf[MAXNUMSTREAMS]; // data for decompressing
  44. // video
  45. HDRAWDIB     ghdd[MAXNUMSTREAMS]; // drawdib handles
  46. int     gcpavi; // # of streams
  47. BOOL     gfPlaying = FALSE; // Are we playing right now?
  48. LONG     glPlayStartTime; // When did we start playing?
  49. LONG      glPlayStartPos; // From what position?
  50. PAVISTREAM          gpaviAudio;                 // 1st audio stream found
  51. PAVISTREAM          gpaviVideo;                 // 1st video stream found
  52. int                 giFirstVideo;                // index of gapavi for 1st Video stream
  53. #define             gfVideoFound (gpaviVideo != NULL)
  54. #define             gfAudioFound (gpaviAudio != NULL)
  55. LONG                timeStart; // cached start, end, length
  56. LONG                timeEnd;
  57. LONG                timeLength;
  58. LONG     timehscroll;                // how much arrows scroll HORZ bar
  59. LONG     vertSBLen;
  60. LONG     vertHeight;
  61. DWORD     gdwMicroSecPerPixel = 1000L;// scale for video
  62. TCHAR               gachFileName[MAX_PATH] = TEXT("");
  63. TCHAR               gachSaveFileName[MAX_PATH] = TEXT("");
  64. UINT     gwZoom = 2; // one half zoom (divide by 4)
  65. AVICOMPRESSOPTIONS  gaAVIOptions[MAXNUMSTREAMS];
  66. LPAVICOMPRESSOPTIONS  galpAVIOptions[MAXNUMSTREAMS];
  67. HFONT               hfontApp;
  68. TEXTMETRIC          tm;
  69. BYTE     abFormat[1024];
  70. LPVOID     lpAudio; // buffer for painting
  71. int                 gnColours;          // No of colours to remap palette to
  72.                  // !!! constants for painting
  73. #define VSPACE  8                 // some vertical spacing
  74. #define HSPACE  4                // space between frames for video streams
  75. #define TSPACE  20                 // space for text area about each stream
  76. #define AUDIOVSPACE  64                 // height of an audio stream at X1 zoom
  77. /*----------------------------------------------------------------------------*
  78. *----------------------------------------------------------------------------*/
  79. #define GetScrollTime(hwnd) 
  80.     (LONG)(timeStart + muldiv32(GetScrollPos(hwnd, SB_HORZ), timeLength, SCROLLRANGE))
  81. #define SetScrollTime(hwnd, time) SetScrollPos(hwnd, SB_HORZ, 
  82.     (int)muldiv32((time) - timeStart, SCROLLRANGE, timeLength), TRUE)
  83. /*----------------------------------------------------------------------------*
  84. *----------------------------------------------------------------------------*/
  85. long             PaintStuff(HDC hdc, HWND hwnd, BOOL fDrawEverything);
  86. LONG WINAPI      AppWndProc (HWND hwnd, UINT uiMessage, WPARAM wParam, LPARAM lParam);
  87. int              ErrMsg (LPTSTR sz,...);
  88. LONG PASCAL      AppCommand(HWND hwnd, unsigned msg, WPARAM wParam, LPARAM lParam);
  89. BOOL CALLBACK    AboutDlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
  90. BOOL CALLBACK    GetNumberOfColorsDlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
  91. LONG             GetNumberOfColors(PAVISTREAM ps);
  92. /*----------------------------------------------------------------------------*
  93. *----------------------------------------------------------------------------*/
  94. HCURSOR hcurSave;
  95. int     fWait = 0;
  96. /*----------------------------------------------------------------------------*
  97. |    StartWait()
  98. |
  99. |    Start a wait operation... put up the hourglass if it's the first call.
  100. *----------------------------------------------------------------------------*/
  101. void StartWait()
  102. {
  103.     if (fWait++ == 0) {
  104.         SetCursor(LoadCursor(NULL,IDC_WAIT));
  105.     }
  106. }
  107. /*----------------------------------------------------------------------------*
  108. |    EndWait()
  109. |
  110. |    Once every one who started a wait is finished, go back to regular cursor.
  111. *----------------------------------------------------------------------------*/
  112. void EndWait()
  113. {
  114.     if (--fWait == 0) {
  115.         SetCursor(LoadCursor(NULL,IDC_ARROW));
  116.         InvalidateRect(ghwndApp, NULL, TRUE);
  117.     }
  118. }
  119. /*----------------------------------------------------------------------------*
  120. |    WinYield()
  121. |
  122. |    Code to yield while we're not calling GetMessage.
  123. |    Dispatch all messages.  Pressing ESC or closing aborts.
  124. *----------------------------------------------------------------------------*/
  125. BOOL WinYield()
  126. {
  127.     MSG msg;
  128.     BOOL fAbort=FALSE;
  129.     while(fWait > 0 && PeekMessage(&msg,NULL,0,0,PM_REMOVE))
  130.     {
  131. if (msg.message == WM_KEYDOWN && msg.wParam == VK_ESCAPE)
  132.             fAbort = TRUE;
  133. if (msg.message == WM_SYSCOMMAND && (msg.wParam & 0xFFF0) == SC_CLOSE)
  134.     fAbort = TRUE;
  135. TranslateMessage(&msg);
  136. DispatchMessage(&msg);
  137.     }
  138.     return fAbort;
  139. }
  140. /*----------------------------------------------------------------------------*
  141. *----------------------------------------------------------------------------*/
  142. /*----------------------------------------------------------------------------*
  143. |    FixScrollbars()
  144. |
  145. |    When we load a file or zoom changes, we re-set the scrollbars.
  146. *----------------------------------------------------------------------------*/
  147. void FixScrollbars(HWND hwnd)
  148. {
  149.     LONG                lHeight = 0;
  150.     RECT                rc;
  151.     HDC                 hdc;
  152.     //
  153.     // Pretend we're painting and count how many lines we needed
  154.     //
  155.     hdc = GetDC(NULL);
  156.     ExcludeClipRect(hdc, 0, 0, 32767, 32767);   // don't actually draw
  157.     lHeight = PaintStuff(hdc, hwnd, TRUE);
  158.     ReleaseDC(NULL, hdc);
  159.     //
  160.     // Set vertical scrollbar for scrolling the visible area
  161.     //
  162.     GetClientRect(hwnd, &rc);
  163.     vertHeight = lHeight; // total height in pixels of entire display
  164.     //
  165.     // We won't fit in the window... need scrollbars
  166.     //
  167.     if (lHeight > rc.bottom) {
  168. vertSBLen = lHeight - rc.bottom;
  169. SetScrollRange(hwnd, SB_VERT, 0, (int)vertSBLen, TRUE);
  170. SetScrollPos(hwnd, SB_VERT, 0, TRUE);
  171.     //
  172.     // We will fit in the window!  No scrollbars necessary
  173.     //
  174.     } else {
  175. vertSBLen = 0;
  176. SetScrollRange(hwnd, SB_VERT, 0, 0, TRUE);
  177.     }
  178. }
  179. /*----------------------------------------------------------------------------*
  180. |    InitStreams()
  181. |
  182. |    Initialize the streams of a loaded file -- the compression options, the
  183. |    DrawDIB handles, and the scroll bars.
  184. *----------------------------------------------------------------------------*/
  185. void InitStreams(HWND hwnd)
  186. {
  187.     AVISTREAMINFO     avis;
  188.     LONG lTemp;
  189.     int i;
  190.     DWORD dw;
  191.     //
  192.     // Start with bogus times
  193.     //
  194.     timeStart = 0x7FFFFFFF;
  195.     timeEnd   = 0;
  196.     //
  197.     // Walk through and init all streams loaded
  198.     //
  199.     for (i = 0; i < gcpavi; i++) {
  200.         AVIStreamInfo(gapavi[i], &avis, sizeof(avis));
  201. //
  202. // Save and SaveOptions code takes a pointer to our compression opts
  203. //
  204. galpAVIOptions[i] = &gaAVIOptions[i];
  205. //
  206. // clear options structure to zeroes
  207. //
  208. _fmemset(galpAVIOptions[i], 0, sizeof(AVICOMPRESSOPTIONS));
  209. //
  210.   // Initialize the compression options to some default stuff
  211. // !!! Pick something better
  212. //
  213. galpAVIOptions[i]->fccType = avis.fccType;
  214. switch(avis.fccType) {
  215.     case streamtypeVIDEO:
  216. galpAVIOptions[i]->dwFlags = AVICOMPRESSF_VALID |
  217. AVICOMPRESSF_KEYFRAMES | AVICOMPRESSF_DATARATE;
  218. galpAVIOptions[i]->fccHandler = 0;
  219. galpAVIOptions[i]->dwQuality = (DWORD)ICQUALITY_DEFAULT;
  220. galpAVIOptions[i]->dwKeyFrameEvery = (DWORD)-1; // Default
  221. galpAVIOptions[i]->dwBytesPerSecond = 0;
  222. galpAVIOptions[i]->dwInterleaveEvery = 1;
  223. break;
  224.     case streamtypeAUDIO:
  225. galpAVIOptions[i]->dwFlags |= AVICOMPRESSF_VALID;
  226. galpAVIOptions[i]->dwInterleaveEvery = 1;
  227.                 AVIStreamReadFormat(gapavi[i], AVIStreamStart(gapavi[i]),
  228.                                     NULL, &lTemp);
  229.                 galpAVIOptions[i]->cbFormat = lTemp;
  230.                 if (lTemp)
  231.                     galpAVIOptions[i]->lpFormat = GlobalAllocPtr(GHND, lTemp);
  232.                 // Use current format as default format
  233.                 if (galpAVIOptions[i]->lpFormat)
  234.                     AVIStreamReadFormat(gapavi[i],
  235.                                     AVIStreamStart(gapavi[i]),
  236.     galpAVIOptions[i]->lpFormat,
  237.     &lTemp);
  238. break;
  239.     default:
  240. break;
  241. }
  242. //
  243. // We're finding the earliest and latest start and end points for
  244. // our scrollbar.
  245. //
  246.         timeStart = min(timeStart, AVIStreamStartTime(gapavi[i]));
  247.         timeEnd   = max(timeEnd, AVIStreamEndTime(gapavi[i]));
  248. //
  249. // Initialize video streams for getting decompressed frames to display
  250. //
  251.         if (avis.fccType == streamtypeVIDEO) {
  252.     gapgf[i] = AVIStreamGetFrameOpen(gapavi[i], NULL);
  253.     if (gapgf[i] == NULL)
  254. continue;
  255.     ghdd[i] = DrawDibOpen();
  256.     // !!! DrawDibBegin?
  257.     if (gpaviVideo == NULL) {
  258. //
  259. // Remember the first video stream --- treat it specially
  260. //
  261.                 gpaviVideo = gapavi[i];
  262.                 giFirstVideo = i;
  263.                 //
  264.                 // Set the horizontal scrollbar scale to show every frame
  265.                 // of the first video stream exactly once
  266.                 //
  267.                 dw = (avis.rcFrame.right - avis.rcFrame.left) * gwZoom / 4 + HSPACE;
  268.                 gdwMicroSecPerPixel = muldiv32(1000000,
  269.                                                avis.dwScale,
  270.                                                dw * avis.dwRate);
  271.                 // Move one frame on the top video screen for each HSCROLL
  272.                 timehscroll = muldiv32(1000, avis.dwScale, avis.dwRate);
  273.             }
  274.         } else if (avis.fccType == streamtypeAUDIO) {
  275.             //
  276.             // If there are no video streams, we base everything on this
  277.             // audio stream.
  278.             //
  279.             if (gpaviAudio == NULL && gpaviVideo == NULL) {
  280.                 // Show one sample per pixel
  281.                 gdwMicroSecPerPixel = muldiv32(1000000,
  282.                                                avis.dwScale,
  283.                                                avis.dwRate);
  284.                 // Move one sample per HSCROLL
  285.                 // Move at least enough to show movement
  286.                 timehscroll = muldiv32(1000, avis.dwScale, avis.dwRate);
  287.             }
  288.     //
  289.     // Remember the first audio stream --- treat it specially
  290.     //
  291.     if (gpaviAudio == NULL)
  292.         gpaviAudio = gapavi[i];
  293. }
  294.     }
  295.     timeLength = timeEnd - timeStart;
  296.     if (timeLength == 0)
  297.         timeLength = 1;
  298.     // Make sure HSCROLL scrolls enough to be noticeable.
  299.     timehscroll = max(timehscroll, timeLength / SCROLLRANGE + 2);
  300.     SetScrollRange(hwnd, SB_HORZ, 0, SCROLLRANGE, TRUE);
  301.     SetScrollTime(hwnd, timeStart);
  302.     FixScrollbars(hwnd);
  303. }
  304. /*----------------------------------------------------------------------------*
  305. |    FixWindowTitle()
  306. |
  307. |    Update the window title to reflect what's loaded.
  308. *----------------------------------------------------------------------------*/
  309. void FixWindowTitle(HWND hwnd)
  310. {
  311.     TCHAR ach[80];
  312.     wsprintf(ach, TEXT("%s %s"),
  313.             (LPTSTR)gszAppName,
  314.             (LPTSTR)gachFileName);
  315.     SetWindowText(hwnd, ach);
  316.     InvalidateRect(hwnd, NULL, TRUE);
  317. }
  318. /*----------------------------------------------------------------------------*
  319. |    FreeDrawStuff()
  320. |
  321. | Free up the resources associated with DrawDIB
  322. *----------------------------------------------------------------------------*/
  323. void FreeDrawStuff(HWND hwnd)
  324. {
  325.     int i;
  326.     aviaudioStop();
  327.     for (i = 0; i < gcpavi; i++) {
  328. if (gapgf[i]) {
  329.     AVIStreamGetFrameClose(gapgf[i]);
  330.     gapgf[i] = NULL;
  331. }
  332. if (ghdd[i]) {
  333.     DrawDibClose(ghdd[i]);
  334.     ghdd[i] = 0;
  335. }
  336.     }
  337.     SetScrollRange(hwnd, SB_HORZ, 0, 0, TRUE);
  338.     gpaviVideo = gpaviAudio = NULL;
  339. }
  340. /*----------------------------------------------------------------------------*
  341. |    FreeAvi()
  342. |
  343. |    Free the resources associated with an open file.
  344. *----------------------------------------------------------------------------*/
  345. void FreeAvi(HWND hwnd)
  346. {
  347.     int i;
  348.     FreeDrawStuff(hwnd);
  349.     AVISaveOptionsFree(gcpavi, galpAVIOptions);
  350.     for (i = 0; i < gcpavi; i++) {
  351. AVIStreamRelease(gapavi[i]);
  352.     }
  353.     if (gpfile)
  354. AVIFileRelease(gpfile);
  355.     gpfile = NULL;
  356.     // Good a place as any to make sure audio data gets freed
  357.     if (lpAudio)
  358.         GlobalFreePtr(lpAudio);
  359.     lpAudio = NULL;
  360.     gcpavi = 0;
  361. }
  362. /*----------------------------------------------------------------------------*
  363. |    InitBall()
  364. |
  365. |    Open up our fake "ball" file as an installible stream hander
  366. *----------------------------------------------------------------------------*/
  367. void InitBall(HWND hwnd)
  368. {
  369.     // close everything down
  370.     FreeAvi(hwnd);
  371.     // The NewBall() function creates a PAVISTREAM we can use as if it was
  372.     // an AVI file.
  373.     gapavi[0] = NewBall();
  374.     if (gapavi[0])
  375. gcpavi = 1;
  376.     lstrcpy(gachFileName, TEXT("BALL"));
  377.     InitStreams(hwnd);
  378.     FixWindowTitle(hwnd);
  379. }
  380. /*----------------------------------------------------------------------------*
  381. |    InsertAVIFile()
  382. |
  383. |    Does most of the work of opening an AVI file.
  384. *----------------------------------------------------------------------------*/
  385. void InsertAVIFile(PAVIFILE pfile, HWND hwnd, LPTSTR lpszFile)
  386. {
  387.     int i;
  388.     PAVISTREAM pavi;
  389.     //
  390.     // Get all the streams from the new file
  391.     //
  392.     for (i = gcpavi; i <= MAXNUMSTREAMS; i++) {
  393. if (AVIFileGetStream(pfile, &pavi, 0L, i - gcpavi) != AVIERR_OK)
  394.     break;
  395.         if (i == MAXNUMSTREAMS) {
  396.             AVIStreamRelease(pavi);
  397.             ErrMsg("Exceeded maximum number of streams");
  398.             break;
  399.         }
  400. gapavi[i] = pavi;
  401.     }
  402.     //
  403.     // Couldn't get any streams out of this file
  404.     //
  405.     if (gcpavi == i && i != MAXNUMSTREAMS)
  406.     {
  407.         ErrMsg(TEXT("Unable to open %s"), lpszFile);
  408. if (pfile)
  409.     AVIFileRelease(pfile);
  410. return;
  411.     }
  412.     gcpavi = i;
  413.     if (gpfile) {
  414. AVIFileRelease(pfile);
  415.     } else
  416. gpfile = pfile;
  417.     FreeDrawStuff(hwnd);
  418.     InitStreams(hwnd);
  419.     FixWindowTitle(hwnd);
  420. }
  421. /*----------------------------------------------------------------------------*
  422. |    InitAvi()
  423. |
  424. |    Open up a file through the AVIFile handlers.
  425. *----------------------------------------------------------------------------*/
  426. void InitAvi(HWND hwnd, LPTSTR szFile, UINT wMenu)
  427. {
  428.     HRESULT hr;
  429.     PAVIFILE pfile;
  430.     hr = AVIFileOpen(&pfile, szFile, 0, 0L);
  431.     if (hr != 0)
  432.     {
  433.         ErrMsg(TEXT("Unable to open %s"), szFile);
  434.         return;
  435.     }
  436.     if (wMenu == MENU_OPEN)
  437. FreeAvi(hwnd);
  438.     InsertAVIFile(pfile, hwnd, szFile);
  439. }
  440. /*----------------------------------------------------------------------------*
  441. |   AppInit( hInst, hPrev)        |
  442. |        |
  443. |   Description:        |
  444. | This is called when the application is first loaded into        |
  445. | memory.  It performs all initialization that doesn't need to be done   |
  446. | once per instance.        |
  447. |        |
  448. |   Arguments:        |
  449. | hInstance instance handle of current instance        |
  450. | hPrev instance handle of previous instance        |
  451. |        |
  452. |   Returns:        |
  453. | TRUE if successful, FALSE if not        |
  454. |        |
  455. *----------------------------------------------------------------------------*/
  456. BOOL AppInit(HINSTANCE hInst, HINSTANCE hPrev, int sw,LPSTR szCmdLine)
  457. {
  458.     WNDCLASS cls;
  459.     HDC hdc;
  460. #ifdef BIDI
  461.     const DWORD  dwExStyle = WS_EX_BIDI_SCROLL  | WS_EX_BIDI_MENU |WS_EX_BIDI_NOICON;
  462. #else
  463.     const DWORD  dwExStyle = 0;
  464. #endif
  465.     /* Save instance handle for DialogBoxs */
  466.     ghInstApp = hInst;
  467.     ghAccel = LoadAccelerators(hInst, MAKEINTATOM(ID_APP));
  468.     if (szCmdLine && szCmdLine[0]) {
  469. #ifdef UNICODE
  470. // convert to unicode
  471. lstrcpy(gachFileName, GetCommandLine());
  472. #else
  473.      lstrcpy(gachFileName, szCmdLine);
  474. #endif
  475.     }
  476.     if (!hPrev) {
  477. /*
  478.  *  Register a class for the main application window
  479.  */
  480.         cls.hCursor        = LoadCursor(NULL,IDC_ARROW);
  481.         cls.hIcon          = LoadIcon(hInst,MAKEINTATOM(ID_APP));
  482.         cls.lpszMenuName   = MAKEINTATOM(ID_APP);
  483.         cls.lpszClassName  = MAKEINTATOM(ID_APP);
  484.         cls.hbrBackground  = (HBRUSH)(COLOR_WINDOW + 1);
  485.         cls.hInstance      = hInst;
  486.         cls.style          = CS_BYTEALIGNCLIENT | CS_VREDRAW | CS_HREDRAW | CS_DBLCLKS;
  487.         cls.lpfnWndProc    = (LPWNDPROC)AppWndProc;
  488.         cls.cbWndExtra     = 0;
  489.         cls.cbClsExtra     = 0;
  490.         if (!RegisterClass(&cls))
  491.     return FALSE;
  492.     }
  493.     //
  494.     // Must be called before using any of the AVIFile routines
  495.     //
  496.     AVIFileInit();
  497.     hfontApp = GetStockObject(ANSI_VAR_FONT);
  498.     hdc = GetDC(NULL);
  499.     SelectObject(hdc, hfontApp);
  500.     GetTextMetrics(hdc, &tm);
  501.     ReleaseDC(NULL, hdc);
  502.     ghwndApp=CreateWindowEx(dwExStyle,
  503.     MAKEINTATOM(ID_APP),    // Class name
  504.                             gszAppName,             // Caption
  505.                             WS_OVERLAPPEDWINDOW,    // Style bits
  506.                             CW_USEDEFAULT, 0,       // Position
  507.                             320,300,                // Size
  508.                             (HWND)NULL,             // Parent window (no parent)
  509.                             (HMENU)NULL,            // use class menu
  510.                             (HANDLE)hInst,          // handle to window instance
  511.                             (LPSTR)NULL             // no params to pass on
  512.                            );
  513.     ShowWindow(ghwndApp,sw);
  514.     ghLib = LoadLibrary(TEXT("palmap32.dll"));
  515.     if (ghLib == NULL)
  516.         gbCanPalMap = FALSE;
  517.     else
  518.         gbCanPalMap = TRUE;
  519.     return TRUE;
  520. }
  521. /*----------------------------------------------------------------------------*
  522. |   WinMain( hInst, hPrev, lpszCmdLine, cmdShow )        |
  523. |                                                                              |
  524. |   Description:                                                               |
  525. |       The main procedure for the App.  After initializing, it just goes      |
  526. |       into a message-processing loop until it gets a WM_QUIT message         |
  527. |       (meaning the app was closed).                                          |
  528. |                                                                              |
  529. |   Arguments:                                                                 |
  530. | hInst instance handle of this instance of the app        |
  531. | hPrev instance handle of previous instance, NULL if first    |
  532. |       szCmdLine       ->null-terminated command line                         |
  533. |       cmdShow         specifies how the window is initially displayed        |
  534. |                                                                              |
  535. |   Returns:                                                                   |
  536. |       The exit code as specified in the WM_QUIT message.                     |
  537. |                                                                              |
  538. *----------------------------------------------------------------------------*/
  539. int PASCAL WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR szCmdLine, int sw)
  540. {
  541.     MSG     msg;
  542.     /* Call initialization procedure */
  543.     if (!AppInit(hInst,hPrev,sw,szCmdLine))
  544.         return FALSE;
  545.     /*
  546.      * Polling messages from event queue
  547.      */
  548.     for (;;)
  549.     {
  550.         while (PeekMessage(&msg, NULL, 0, 0,PM_REMOVE))
  551.         {
  552.             if (msg.message == WM_QUIT)
  553.                 return msg.wParam;
  554.     if (TranslateAccelerator(ghwndApp, ghAccel, &msg))
  555. continue;
  556.             TranslateMessage(&msg);
  557.             DispatchMessage(&msg);
  558.         }
  559. //
  560. // If we have no messages to dispatch, we do our background task...
  561. // If we're playing a file, we set the scroll bar to show the video
  562. // frames corresponding with the current playing audio sample
  563. //
  564.         if (gfPlaying) {
  565.     LONG    l;
  566.     //
  567.     // Use the audio clock to tell how long we've been playing.  To
  568.     // maintain sync, it's important we use this clock.
  569.     //
  570.     l = aviaudioTime();  // returns -1 if no audio playing
  571.     //
  572.     // If we can't use the audio clock to tell us how long we've been
  573.     // playing, calculate it ourself
  574.     //
  575.     if (l == -1)
  576. l = timeGetTime() - glPlayStartTime + glPlayStartPos;
  577.     if (l != GetScrollTime(ghwndApp)) {
  578.         if (l < timeStart) // make sure number isn't out of bounds
  579.     l = timeStart;
  580.         if (l > timeEnd) // looks like we're all done!
  581.                     FORWARD_WM_COMMAND(ghwndApp, MENU_STOP, NULL, 0, SendMessage);
  582. SetScrollTime(ghwndApp, l);
  583. InvalidateRect(ghwndApp, NULL, FALSE);
  584. UpdateWindow(ghwndApp);
  585. continue;
  586.     }
  587. }
  588. WaitMessage();
  589.     }
  590.     // not reached
  591.     return msg.wParam;
  592. }
  593. typedef BYTE * HPBYTE;
  594. typedef UNALIGNED short * HPSHORT;
  595. /*----------------------------------------------------------------------------*
  596. |    PaintVideo()
  597. |
  598. |    Draw a video frame in the specified rect.
  599. *----------------------------------------------------------------------------*/
  600. void PaintVideo(HDC hdc, RECT rcFrame, int iStream, LPBITMAPINFOHEADER lpbi, LONG lCurFrame, LONG lPos)
  601. {
  602.     int iLen;
  603.     char ach[200];
  604.     RECT rc;
  605.     COLORREF nCol;
  606.     //
  607.     // If we have a picture, draw it
  608.     //
  609.     if (lpbi)
  610.     {
  611.         //
  612.         // use the palette of the first video stream
  613.         //
  614.         DrawDibDraw(ghdd[iStream],hdc,
  615.     rcFrame.left, rcFrame.top,
  616.     rcFrame.right - rcFrame.left,
  617.     rcFrame.bottom - rcFrame.top,
  618.     lpbi, NULL,
  619.     0, 0, -1, -1,
  620.     gapavi[iStream] == gpaviVideo ? 0 : DDF_BACKGROUNDPAL);
  621.         iLen = wsprintf(ach, TEXT("%ld %ld.%03lds"),
  622.     lCurFrame, lPos/1000, lPos%1000);
  623.     }
  624.     //
  625.     // Before or after the movie (or read error) draw GRAY
  626.     //
  627.     else {
  628.         if (gapgf[iStream])
  629.     SelectObject(hdc,GetStockObject(DKGRAY_BRUSH));
  630.         else
  631.     SelectObject(hdc,GetStockObject(LTGRAY_BRUSH));
  632.         PatBlt(hdc,
  633.     rcFrame.left, rcFrame.top,
  634.     rcFrame.right - rcFrame.left,
  635.     rcFrame.bottom - rcFrame.top,
  636.     PATCOPY);
  637.         iLen = 0;
  638.         ach[0] = TEXT('');
  639.     }
  640.     //
  641.     // print something meaningful under the frame
  642.     //
  643.     rc.left = rcFrame.left;
  644.     rc.right = rcFrame.right + HSPACE;
  645.     rc.top = rcFrame.bottom + VSPACE;
  646.     rc.bottom = rc.top + TSPACE;
  647.     nCol = SetBkColor(hdc, GetSysColor(COLOR_WINDOW));
  648.     ExtTextOut(hdc, rc.left, rc.top, ETO_OPAQUE,
  649.        &rc, ach, iLen, NULL);
  650.     SetBkColor(hdc, nCol);
  651. }
  652. /*----------------------------------------------------------------------------*
  653. |    PaintAudio()
  654. |
  655. |    Draw some samples of audio inside the given rectangle.
  656. *----------------------------------------------------------------------------*/
  657. void PaintAudio(HDC hdc, PRECT prc, PAVISTREAM pavi, LONG lStart, LONG lLen)
  658. {
  659.     PCMWAVEFORMAT wf;
  660.     int i;
  661.     int x,y;
  662.     int w,h;
  663.     BYTE b;
  664.     HBRUSH hbr;
  665.     RECT rc = *prc;
  666.     LONG    lBytes;
  667.     LONG    l, lLenOrig = lLen;
  668.     LONG    lWaveBeginTime = AVIStreamStartTime(pavi);
  669.     LONG    lWaveEndTime   = AVIStreamEndTime(pavi);
  670.     //
  671.     // We can't draw before the beginning of the stream - adjust
  672.     //
  673.     if (lStart < lWaveBeginTime) {
  674. lLen -= lWaveBeginTime - lStart;
  675. lStart = lWaveBeginTime;
  676. // right justify the legal samples in the rectange - don't stretch
  677. rc.left = rc.right - (int)muldiv32(rc.right - rc.left, lLen, lLenOrig);
  678.     }
  679.     //
  680.     // We can't draw past the end of the stream
  681.     //
  682.     if (lStart + lLen > lWaveEndTime) {
  683. lLenOrig = lLen;
  684. lLen = max(0, lWaveEndTime - lStart); // maybe nothing to draw!
  685. // left justify the legal samples in the rectange - don't stretch
  686. rc.right = rc.left + (int)muldiv32(rc.right - rc.left, lLen, lLenOrig);
  687.     }
  688.     // Now change and work with samples, not time.
  689.     l = lStart;
  690.     lStart = AVIStreamTimeToSample(pavi, lStart);
  691.     lLen = AVIStreamTimeToSample(pavi, l + lLen) - lStart;
  692.     //
  693.     // Get the format of the wave data
  694.     //
  695.     l = sizeof(wf);
  696.     AVIStreamReadFormat(pavi, lStart, &wf, &l);
  697.     if (!l)
  698.         return;
  699.     w = rc.right - rc.left;
  700.     h = rc.bottom - rc.top;
  701.     //
  702.     // We were starting before the beginning or continuing past the end.
  703.     // We're not painting in the whole original rect --- use a dark background
  704.     //
  705.     if (rc.left > prc->left) {
  706.         SelectObject(hdc, GetStockObject(DKGRAY_BRUSH));
  707.   PatBlt(hdc, prc->left, rc.top, rc.left - prc->left,
  708.     rc.bottom - rc.top, PATCOPY);
  709.     }
  710.     if (rc.right < prc->right) {
  711.         SelectObject(hdc, GetStockObject(DKGRAY_BRUSH));
  712.  PatBlt(hdc, rc.right, rc.top, prc->right - rc.right,
  713.     rc.bottom - rc.top, PATCOPY);
  714.     }
  715. #define BACKBRUSH  (GetSysColor(COLOR_BTNFACE))
  716. #define MONOBRUSH  (GetSysColor(COLOR_BTNSHADOW))
  717. #define LEFTBRUSH  (RGB(0,0,255))
  718. #define RIGHTBRUSH (RGB(0,255,0))
  719. #define HPOSBRUSH  (RGB(255,0,0))
  720.     //
  721.     // Paint the background
  722.     //
  723.     hbr = SelectObject(hdc, CreateSolidBrush(BACKBRUSH));
  724.     PatBlt(hdc, rc.left, rc.top, w, h, PATCOPY);
  725.     DeleteObject(SelectObject(hdc, hbr));
  726.     //
  727.     // !!! we can only paint PCM data right now.  Sorry!
  728.     //
  729.     if (wf.wf.wFormatTag != WAVE_FORMAT_PCM)
  730.         return;
  731.     //
  732.     // How many bytes are we painting? Alloc some space for them
  733.     //
  734.     lBytes = lLen * wf.wf.nChannels * wf.wBitsPerSample / 8;
  735.     if (!lpAudio)
  736.         lpAudio = GlobalAllocPtr (GHND, lBytes);
  737.     else if ((LONG)GlobalSizePtr(lpAudio) < lBytes)
  738.         lpAudio = GlobalReAllocPtr(lpAudio, lBytes, GMEM_MOVEABLE);
  739.     if (!lpAudio)
  740.         return;
  741.     //
  742.     // Read in the wave data
  743.     //
  744.     AVIStreamRead(pavi, lStart, lLen, lpAudio, lBytes, NULL, &l);
  745.     if (l != lLen)
  746. return;
  747.     //
  748.     // !!! Flickers less painting it NOW or LATER?
  749.     // First show the current position as a bar
  750.     //
  751.     hbr = SelectObject(hdc, CreateSolidBrush(HPOSBRUSH));
  752.     PatBlt(hdc, prc->right / 2, prc->top, 1, prc->bottom - prc->top, PATCOPY);
  753.     DeleteObject(SelectObject(hdc, hbr));
  754.     //
  755.     // Paint monochrome wave data
  756.     //
  757.     if (wf.wf.nChannels == 1) {
  758. //
  759. // Draw the x-axis
  760. //
  761.         hbr = SelectObject(hdc, CreateSolidBrush(MONOBRUSH));
  762.         y = rc.top + h/2;
  763.         PatBlt(hdc, rc.left, y, w, 1, PATCOPY);
  764. //
  765. // 8 bit data is centred around 0x80
  766. //
  767.         if (wf.wBitsPerSample == 8) {
  768.             for (x=0; x<w; x++) {
  769. // which byte of audio data belongs at this pixel?
  770.                 b = *((HPBYTE)lpAudio + muldiv32(x,lLen,w));
  771.                 if (b > 0x80) {
  772.                     i = y - (int)muldiv32(b-0x80,(h/2),128);
  773.                     PatBlt(hdc, rc.left+x, i, 1, y-i, PATCOPY);
  774.                 }
  775.                 else {
  776.                     i = y + (int)muldiv32(0x80-b,(h/2),128);
  777.                     PatBlt(hdc, rc.left+x, y, 1, i-y, PATCOPY);
  778.                 }
  779.             }
  780.         }
  781. //
  782. // 16 bit data is centred around 0x00
  783. //
  784.         else if (wf.wBitsPerSample == 16) {
  785.             for (x=0; x<w; x++) {
  786. // Don't make any assumptions about INT size !
  787. // which byte of audio data belongs at this pixel?
  788.                 i = *((HPSHORT)lpAudio + muldiv32(x,lLen,w));
  789.                 if (i > 0) {
  790.                    i = y - (int) ((LONG)i * (h/2) / 32768);
  791.                    PatBlt(hdc, rc.left+x, i, 1, y-i, PATCOPY);
  792.                 }
  793.                 else {
  794.                    i = (int) ((LONG)i * (h/2) / 32768);
  795.                    PatBlt(hdc, rc.left+x, y, 1, -i, PATCOPY);
  796.                 }
  797.             }
  798.         }
  799.         DeleteObject(SelectObject(hdc, hbr));
  800.     } // endif mono
  801.     //
  802.     // Draw stereo waveform data
  803.     //
  804.     else if (wf.wf.nChannels == 2) {
  805. //
  806. // 8 bit data is centred around 0x80
  807. //
  808.         if (wf.wBitsPerSample == 8) {
  809.             // Left channel
  810.             hbr = SelectObject(hdc, CreateSolidBrush(LEFTBRUSH));
  811.             y = rc.top + h/4;
  812.             PatBlt(hdc, rc.left, y, w, 1, PATCOPY);
  813.             for (x=0; x<w; x++) {
  814.                 b = *((HPBYTE)lpAudio + muldiv32(x,lLen,w) * 2);
  815.                 if (b > 0x80) {
  816.                     i = y - (int)muldiv32(b-0x80,(h/4),128);
  817.                     PatBlt(hdc, rc.left+x, i, 1, y-i, PATCOPY);
  818.                 }
  819.                 else {
  820.                     i = y + (int)muldiv32(0x80-b,(h/4),128);
  821.                     PatBlt(hdc, rc.left+x, y, 1, i-y, PATCOPY);
  822.                 }
  823.             }
  824.             DeleteObject(SelectObject(hdc, hbr));
  825.             // Right channel
  826.             hbr = SelectObject(hdc, CreateSolidBrush(RIGHTBRUSH));
  827.             y = rc.top + h * 3 / 4;
  828.             PatBlt(hdc, rc.left, y, w, 1, PATCOPY);
  829.             for (x=0; x<w; x++) {
  830.                 b = *((HPBYTE)lpAudio + muldiv32(x,lLen,w) * 2 + 1);
  831.                 if (b > 0x80) {
  832.                     i = y - (int)muldiv32(b-0x80,(h/4),128);
  833.                     PatBlt(hdc, rc.left+x, i, 1, y-i, PATCOPY);
  834.                 }
  835.                 else {
  836.                     i = y + (int)muldiv32(0x80-b,(h/4),128);
  837.                     PatBlt(hdc, rc.left+x, y, 1, i-y, PATCOPY);
  838.                 }
  839.             }
  840.             DeleteObject(SelectObject(hdc, hbr));
  841.         }
  842. //
  843. // 16 bit data is centred around 0x00
  844. //
  845.         else if (wf.wBitsPerSample == 16) {
  846.             // Left channel
  847.             hbr = SelectObject(hdc, CreateSolidBrush(LEFTBRUSH));
  848.             y = rc.top + h/4;
  849.             PatBlt(hdc, rc.left, y, w, 1, PATCOPY);
  850.             for (x=0; x<w; x++) {
  851. // Don't make any assumptions about INT size !
  852.                 i = *((HPSHORT)lpAudio + muldiv32(x,lLen,w) * 2);
  853.                 if (i > 0) {
  854.                     i = y - (int) ((LONG)i * (h/4) / 32768);
  855.                     PatBlt(hdc, rc.left+x, i, 1, y-i, PATCOPY);
  856.                 }
  857.                 else {
  858.                     i = (int) ((LONG)i * (h/4) / 32768);
  859.                     PatBlt(hdc, rc.left+x, y, 1, -i, PATCOPY);
  860.                 }
  861.             }
  862.             DeleteObject(SelectObject(hdc, hbr));
  863.             // Right channel
  864.             hbr = SelectObject(hdc, CreateSolidBrush(RIGHTBRUSH));
  865.             y = rc.top + h * 3 / 4;
  866.             PatBlt(hdc, rc.left, y, w, 1, PATCOPY);
  867.             for (x=0; x<w; x++) {
  868. // Don't make any assumptions about INT size !
  869.                 i = *((HPSHORT)lpAudio + muldiv32(x,lLen,w) * 2 + 1);
  870.                 if (i > 0) {
  871.                    i = y - (int) ((LONG)i * (h/4) / 32768);
  872.                    PatBlt(hdc, rc.left+x, i, 1, y-i, PATCOPY);
  873.                 }
  874.                 else {
  875.                    i = (int) ((LONG)i * (h/4) / 32768);
  876.                    PatBlt(hdc, rc.left+x, y, 1, -i, PATCOPY);
  877.                 }
  878.             }
  879.             DeleteObject(SelectObject(hdc, hbr));
  880.         }
  881.     } // endif stereo
  882. }
  883. /*----------------------------------------------------------------------------*
  884. |    PaintStuff()
  885. |
  886. |    Paint the screen with what we plan to show them.
  887. *----------------------------------------------------------------------------*/
  888. long PaintStuff(HDC hdc, HWND hwnd, BOOL fDrawEverything)
  889. {
  890.     int         yStreamTop;
  891.     TCHAR       ach[400];
  892.     int         iFrameWidth, iLen;
  893.     int         n;
  894.     int         nFrames;
  895.     LPBITMAPINFOHEADER lpbi;
  896.     LONG        lTime;
  897.     LONG        lSize = 0;
  898.     LONG        lAudioStart;
  899.     LONG        lAudioLen;
  900.     RECT        rcFrame, rcC;
  901.     int         i;
  902.     LONG        lFrame;
  903.     LONG        lCurFrame;
  904.     HBRUSH      hbr;
  905.     RECT        rc;
  906.     COLORREF nCol;
  907.     SelectObject(hdc, hfontApp);
  908.     #define PRINT(sz) 
  909.         (nCol = SetBkColor(hdc, GetSysColor(COLOR_WINDOW)), 
  910. TextOut(hdc, TSPACE, yStreamTop, sz, lstrlen(sz)), 
  911.         SetBkColor(hdc, nCol),    
  912. yStreamTop += tm.tmHeight+1)
  913.     #define PF1(sz,a)                   (wsprintf(ach, sz, a), PRINT(ach))
  914.     #define PF2(sz,a,b)                 (wsprintf(ach, sz, a, b), PRINT(ach))
  915.     #define PF3(sz,a,b,c)               (wsprintf(ach, sz, a, b, c), PRINT(ach))
  916.     #define PF4(sz,a,b,c,d)             (wsprintf(ach, sz, a, b, c, d), PRINT(ach))
  917.     #define PF5(sz,a,b,c,d,e)           (wsprintf(ach, sz, a, b, c, d, e), PRINT(ach))
  918.     #define PF6(sz,a,b,c,d,e,f)         (wsprintf(ach, sz, a, b, c, d, e, f), PRINT(ach))
  919.     #define PF7(sz,a,b,c,d,e,f,g)       (wsprintf(ach, sz, a, b, c, d, e, f, g), PRINT(ach))
  920.     #define PF8(sz,a,b,c,d,e,f,g,h)     (wsprintf(ach, sz, a, b, c, d, e, f, g, h), PRINT(ach))
  921.     #define PF9(sz,a,b,c,d,e,f,g,h,i)   (wsprintf(ach, sz, a, b, c, d, e, f, g, h, i), PRINT(ach))
  922.     GetClientRect(hwnd, &rcC);
  923.     //
  924.     // Look at scrollbars to find current position
  925.     //
  926.     lTime = GetScrollTime(hwnd);
  927.     yStreamTop = -GetScrollPos(hwnd, SB_VERT);
  928.     //
  929.     // Walk through all streams and draw something
  930.     //
  931.     for (i=0; i<gcpavi; i++) {
  932. AVISTREAMINFO avis;
  933. LONG lEnd, lEndTime, lNextFmt, lPrevFmt, l;
  934. LONG lPos, lNextKey, lPrevKey, lNextAny, lPrevAny;
  935. AVIStreamInfo(gapavi[i], &avis, sizeof(avis));
  936. FIXCC(avis.fccHandler);
  937. FIXCC(avis.fccType);
  938. l = sizeof(abFormat);
  939. AVIStreamReadFormat(gapavi[i],0, &abFormat, &l);
  940. PF7(TEXT("Stream%d [%4.4hs/%4.4hs] Start: %ld Length: %ld (%ld.%03ld sec)             "),
  941.     i,
  942.     (LPSTR)&avis.fccType,
  943.     (LPSTR)&avis.fccHandler,
  944.     AVIStreamStart(gapavi[i]),
  945.     AVIStreamLength(gapavi[i]),
  946.     AVIStreamLengthTime(gapavi[i]) / 1000,
  947.     AVIStreamLengthTime(gapavi[i]) % 1000);
  948. lPos = AVIStreamTimeToSample(gapavi[i], lTime);
  949. AVIStreamSampleSize(gapavi[i], lPos, &lSize);
  950. lPrevKey = AVIStreamFindSample(gapavi[i], lPos, FIND_PREV|FIND_KEY);
  951. lPrevAny = AVIStreamFindSample(gapavi[i], lPos, FIND_PREV|FIND_ANY);
  952. lPrevFmt = AVIStreamFindSample(gapavi[i], lPos, FIND_PREV|FIND_FORMAT);
  953. lNextKey = AVIStreamFindSample(gapavi[i], lPos, FIND_NEXT|FIND_KEY);
  954. lNextAny = AVIStreamFindSample(gapavi[i], lPos, FIND_NEXT|FIND_ANY);
  955. lNextFmt = AVIStreamFindSample(gapavi[i], lPos, FIND_NEXT|FIND_FORMAT);
  956. PF5(TEXT("Pos:%ld Time:%ld.%03ld sec Size:%ld bytes %s                                 "),
  957. lPos, lTime/1000, lTime%1000, lSize,
  958. (LPTSTR)(lPos == lPrevKey ? TEXT("Key") : TEXT("")));
  959. PF6(TEXT("PrevKey=%ld NextKey=%ld, PrevAny=%ld NextAny=%ld, PrevFmt=%ld NextFmt=%ld                      "),
  960.     lPrevKey, lNextKey,
  961.     lPrevAny, lNextAny,
  962.     lPrevFmt, lNextFmt);
  963. //
  964. // Draw a VIDEO stream
  965. //
  966. if (avis.fccType == streamtypeVIDEO) {
  967.     if (gapgf[i] == NULL)
  968. continue;
  969.     lpbi = (LPBITMAPINFOHEADER)abFormat;
  970.     FIXCC(lpbi->biCompression);
  971.     //
  972.     // display video format
  973.     //
  974.     //  Video: 160x120x8 (cram)
  975.     //
  976.     PF4(TEXT("Format: %dx%dx%d (%4.4hs)"),
  977. (int)lpbi->biWidth,
  978. (int)lpbi->biHeight,
  979. (int)lpbi->biBitCount,
  980. (LPSTR)&lpbi->biCompression);
  981.             //
  982.     // Which frame belongs at this time?
  983.     //
  984.     lEndTime = AVIStreamEndTime(gapavi[i]);
  985.     if (lTime <= lEndTime)
  986. lFrame = AVIStreamTimeToSample(gapavi[i], lTime);
  987.     else { // we've scrolled past the end of this stream
  988. lEnd = AVIStreamEnd(gapavi[i]);
  989. lFrame = lEnd + AVIStreamTimeToSample(
  990. gapavi[i], lTime - lEndTime);
  991.     }
  992.     //
  993.     // how wide is each frame to paint?
  994.     //
  995.     iFrameWidth = (avis.rcFrame.right - avis.rcFrame.left) *
  996. gwZoom / 4 + HSPACE;
  997.     //
  998.     // how many frames can we fit on each half of the screen?
  999.     //
  1000.     nFrames = (rcC.right - iFrameWidth) / (2 * iFrameWidth);
  1001.     if (nFrames < 0)
  1002. nFrames = 0;    // at least draw *something*
  1003.     //
  1004.     // Step through all the frames we'll draw
  1005.     //
  1006.     for (n=-nFrames; n<=nFrames; n++)
  1007.     {
  1008.                 //
  1009.                 // Each video stream is drawn as a horizontal line of
  1010.                 // frames, very close together.
  1011.                 // The first video stream shows a different frame in
  1012.                 // each square. Thus the scale of time is determined
  1013.                 // by the first video stream.
  1014.                 // Every other video stream shows whatever
  1015.                 // frame belongs at the time corresponding to the mid-
  1016.                 // point of each square.
  1017.                 //
  1018. if (gapavi[i] == gpaviVideo) {
  1019.                     //
  1020.                     // by definition, we know what frame we're drawing..
  1021.                     // (lFrame-n), (lFrame-(n-1)), ..., (lFrame), ...,
  1022.                     // (lFrame+(n-1)), (lFrame+n)
  1023.                     //
  1024.     lCurFrame = lFrame + n;
  1025.     //
  1026.     // what time is it at that frame?  This number will be
  1027.     // printed underneath the frame
  1028.     //
  1029.     l = AVIStreamSampleToTime(gapavi[i], lCurFrame);
  1030. } else { // NOT the first video stream
  1031.     //
  1032.     // What time is it at the left edge of the square
  1033.     // we'll draw?  That's what frame we use.
  1034.     //
  1035.                     if (n<0) {
  1036.                         l = lTime - muldiv32(-n * iFrameWidth + HSPACE,
  1037. gdwMicroSecPerPixel, 1000);
  1038.                     }
  1039.                     else {
  1040.         l = lTime + muldiv32(n * iFrameWidth,
  1041. gdwMicroSecPerPixel, 1000);
  1042.                     }
  1043.                     //
  1044.     // What frame belongs to that time?
  1045.     //
  1046.     lCurFrame = AVIStreamTimeToSample(gapavi[i], l);
  1047.                     //
  1048.                     // what time is it at that frame? This number will
  1049.                     // be printed underneath the frame
  1050.                     //
  1051.                     l = AVIStreamSampleToTime(gapavi[i], lCurFrame);
  1052. }
  1053. // !!!
  1054. // Could actually return an LPBI for invalid frames
  1055. // so we better force it to NULL.
  1056. //
  1057. if (gapgf[i] && lCurFrame >= AVIStreamStart(gapavi[i]))
  1058.     lpbi = AVIStreamGetFrame(gapgf[i], lCurFrame);
  1059. else
  1060.     lpbi = NULL;
  1061. //
  1062. // Figure out where to draw this frame
  1063. //
  1064. rcFrame.left   = rcC.right / 2 -
  1065.     ((avis.rcFrame.right - avis.rcFrame.left) * gwZoom / 4)
  1066.     / 2 + (n * iFrameWidth);
  1067. rcFrame.top    = yStreamTop;
  1068. rcFrame.right  = rcFrame.left +
  1069. (avis.rcFrame.right - avis.rcFrame.left)*gwZoom/4;
  1070. rcFrame.bottom = rcFrame.top +
  1071. (avis.rcFrame.bottom - avis.rcFrame.top)*gwZoom/4;
  1072. //
  1073. // draw a border around the current frame.  Make the
  1074. // one around the centre frame a special colour.
  1075. //
  1076. if (n == 0)
  1077.     hbr = CreateSolidBrush(RGB(255,0,0));
  1078. else
  1079.     hbr = CreateSolidBrush(RGB(255,255,255));
  1080. InflateRect(&rcFrame, 1, 1);
  1081. FrameRect(hdc, &rcFrame, hbr);
  1082. InflateRect(&rcFrame, -1, -1);
  1083. DeleteObject (hbr);
  1084. //
  1085. // Now draw the video frame in the computed rectangle
  1086. //
  1087. PaintVideo(hdc, rcFrame, i, lpbi, lCurFrame, l);
  1088.     }
  1089.     //
  1090.     // Move down to where we can draw the next stream
  1091.     //
  1092.     yStreamTop += (rcFrame.bottom - rcFrame.top) +
  1093. TSPACE;
  1094. }
  1095. //
  1096. // Draw an AUDIO stream
  1097. //
  1098. else if (avis.fccType == streamtypeAUDIO) {
  1099.     LPWAVEFORMAT pwf = (LPWAVEFORMAT)abFormat;
  1100.     TCHAR *szFmt;
  1101.     if (pwf->wFormatTag == 1) {  // PCM
  1102. if (pwf->nChannels == 1)
  1103.     szFmt = TEXT("Format: Mono %ldHz %dbit");
  1104. else
  1105.     szFmt = TEXT("Format: Stereo %ldHz %dbit");
  1106.     }
  1107.     else if (pwf->wFormatTag == 2) {  // ADPCM
  1108. if (pwf->nChannels == 1)
  1109.     szFmt = TEXT("Format: ADPCM Mono %ldHz %dbit");
  1110. else
  1111.     szFmt = TEXT("Format: ADPCM Stereo %ldHz %dbit");
  1112.     }
  1113.     else {
  1114. if (pwf->nChannels == 1)
  1115.     szFmt = TEXT("Format: Compressed Mono %ldHz %dbit");
  1116. else
  1117.     szFmt = TEXT("Format: Compressed Stereo %ldHz %dbit");
  1118.     }
  1119.     PF2(szFmt,(LONG)pwf->nSamplesPerSec,
  1120. (int)((LONG)pwf->nAvgBytesPerSec / pwf->nSamplesPerSec /
  1121.       pwf->nChannels) * 8);
  1122.     //
  1123.     // Figure out which samples are visible
  1124.     //
  1125.     lAudioStart = lTime - muldiv32(rcC.right / 2,
  1126. gdwMicroSecPerPixel, 1000);
  1127.     lAudioLen = 2 * (lTime - lAudioStart);
  1128.     //
  1129.     // Make rectangle to draw audio into
  1130.     //
  1131.     rc.left = rcC.left;
  1132.     rc.right = rcC.right;
  1133.     rc.top = yStreamTop;
  1134.     rc.bottom = rc.top + AUDIOVSPACE * gwZoom / 4;
  1135.     //
  1136.     // Actually paint the audio
  1137.     //
  1138.     PaintAudio(hdc, &rc, gapavi[i], lAudioStart, lAudioLen);
  1139.             //
  1140.             // Move down to where we can draw the next stream
  1141.             //
  1142.             yStreamTop += AUDIOVSPACE * gwZoom / 4;
  1143. }
  1144. else if (avis.fccType == streamtypeTEXT) {
  1145.     LONG    lPos;
  1146.     int     iLeft;
  1147.     lPos = AVIStreamTimeToSample(gapavi[i],
  1148.  lTime -
  1149.  muldiv32((rcC.right - rcC.left),
  1150. gdwMicroSecPerPixel,
  1151. 1000));
  1152.     if (lPos < 0)
  1153. lPos = 0;
  1154.     PatBlt(hdc, rcC.left, yStreamTop,
  1155.    rcC.right - rcC.left, TSPACE + TSPACE,
  1156.    WHITENESS);
  1157.     while (lPos < AVIStreamEnd(gapavi[i]) - 1) {
  1158.                 LONG    lStreamTime = AVIStreamSampleToTime(gapavi[i], lPos);
  1159.                 // What pixel is it at this time?
  1160.                 if (lStreamTime < lTime) {              // pixel will be left of centre
  1161.                     iLeft = (rcC.right + rcC.left) / 2 -
  1162.     (int) muldiv32(lTime - lStreamTime, 1000,  gdwMicroSecPerPixel);
  1163.                 }
  1164.                 else {                                  // pixel is at, or right of, centre
  1165.                     iLeft = (rcC.right + rcC.left) / 2 +
  1166.     (int) muldiv32(lStreamTime - lTime, 1000,  gdwMicroSecPerPixel);
  1167.                 }
  1168. if (iLeft >= rcC.right)
  1169.     break;
  1170. AVIStreamRead(gapavi[i], lPos, 1, ach, sizeof(ach), &l, NULL);
  1171. nCol = SetBkColor(hdc, GetSysColor(COLOR_WINDOW));
  1172. if (l)
  1173.     TextOut(hdc, iLeft, yStreamTop, ach, (int) l - 1);
  1174. iLen = wsprintf(ach, TEXT("%ld"), lPos);
  1175. TextOut(hdc, iLeft, yStreamTop + TSPACE, ach, iLen);
  1176. SetBkColor(hdc, nCol);
  1177. lPos += 1;
  1178.     }
  1179.     yStreamTop += TSPACE + TSPACE;
  1180. }
  1181. yStreamTop += VSPACE;
  1182.         //
  1183.         // Give up once we're painting below the bottom of the window
  1184.         //
  1185.         if (!fDrawEverything && yStreamTop >= rcC.bottom)
  1186.             break;
  1187.     }
  1188.     //
  1189.     // How many lines did we draw?
  1190.     //
  1191.     return yStreamTop + GetScrollPos(hwnd, SB_VERT);
  1192. }
  1193. /*----------------------------------------------------------------------------*
  1194. |   AppWndProc( hwnd, uiMessage, wParam, lParam )        |
  1195. |                                                                              |
  1196. |   Description:                                                               |
  1197. |       The window proc for the app's main (tiled) window.  This processes all |
  1198. |       of the parent window's messages.                                       |
  1199. |                                                                              |
  1200. |   Arguments:                                                                 |
  1201. | hwnd window handle for the window        |
  1202. |       msg             message number                                         |
  1203. |       wParam          message-dependent                                      |
  1204. |       lParam          message-dependent                                      |
  1205. |                                                                              |
  1206. |   Returns:                                                                   |
  1207. |       0 if processed, nonzero if ignored                                     |
  1208. |                                                                              |
  1209. *----------------------------------------------------------------------------*/
  1210. LONG WINAPI  AppWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
  1211. {
  1212.     PAINTSTRUCT ps;
  1213.     BOOL        f;
  1214.     HDC hdc;
  1215.     RECT rc;
  1216.     LONG l;
  1217.     switch (msg) {
  1218.         //
  1219.         // If we passed a command line filename, open it
  1220.         //
  1221.         case WM_CREATE:
  1222.             if (gachFileName[0])
  1223.                 InitAvi(hwnd, gachFileName, MENU_OPEN);
  1224.     break;
  1225.         case WM_COMMAND:
  1226.             return AppCommand(hwnd,msg,wParam,lParam);
  1227.         case WM_INITMENU:
  1228.             f = gfVideoFound || gfAudioFound;
  1229.             EnableMenuItem((HMENU)wParam, MENU_OPTIONS,f ? MF_ENABLED :
  1230. MF_GRAYED);
  1231.             f = gcpavi > 0;
  1232.             EnableMenuItem((HMENU)wParam, MENU_SAVEAS, f ? MF_ENABLED :
  1233. MF_GRAYED);
  1234.             EnableMenuItem((HMENU)wParam, MENU_NEW,    f ? MF_ENABLED :
  1235. MF_GRAYED);
  1236.             EnableMenuItem((HMENU)wParam, MENU_ADD,    f ? MF_ENABLED :
  1237. MF_GRAYED);
  1238.             f = (gpfile != 0) && gbCanPalMap && gfVideoFound;
  1239.             EnableMenuItem((HMENU)wParam, MENU_NEWPALETTE, f ? MF_ENABLED :
  1240.                         MF_GRAYED);
  1241.     f = gfVideoFound || gfAudioFound;
  1242.             EnableMenuItem((HMENU)wParam, MENU_PLAY,
  1243. (f && !gfPlaying) ? MF_ENABLED : MF_GRAYED);
  1244.             EnableMenuItem((HMENU)wParam, MENU_STOP,
  1245.                         (f && gfPlaying) ? MF_ENABLED : MF_GRAYED);
  1246.     CheckMenuItem((HMENU)wParam, MENU_ZOOMQUARTER,
  1247.     (gwZoom == 1) ? MF_CHECKED : MF_UNCHECKED);
  1248.     CheckMenuItem((HMENU)wParam, MENU_ZOOMHALF,
  1249.     (gwZoom == 2) ? MF_CHECKED : MF_UNCHECKED);
  1250.     CheckMenuItem((HMENU)wParam, MENU_ZOOM1,
  1251.     (gwZoom == 4) ? MF_CHECKED : MF_UNCHECKED);
  1252.     CheckMenuItem((HMENU)wParam, MENU_ZOOM2,
  1253.     (gwZoom == 8) ? MF_CHECKED : MF_UNCHECKED);
  1254.     CheckMenuItem((HMENU)wParam, MENU_ZOOM4,
  1255.     (gwZoom == 16) ? MF_CHECKED : MF_UNCHECKED);
  1256.     
  1257.             break;
  1258.         //
  1259.         // During a wait state (eg saving) don't let us choose any menus
  1260.         //
  1261. case WM_NCHITTEST:
  1262.     if (fWait) {
  1263. // Let windows tell us where the cursor is
  1264. lParam = DefWindowProc(hwnd,msg,wParam,lParam);
  1265. // If it's over a menu, pretend it's in the client (force
  1266. // an hourglass)
  1267. if (lParam == HTMENU)
  1268.     lParam = HTCLIENT;
  1269. return lParam;
  1270.     }
  1271.     break;
  1272. //
  1273. // Set vertical scrollbar for scrolling streams
  1274. //
  1275. case WM_SIZE:
  1276.     GetClientRect(hwnd, &rc);
  1277.             //
  1278.             // There is not enough vertical room to show all streams. Scrollbars
  1279.             // are required.
  1280.             //
  1281.     if (vertHeight > rc.bottom) {
  1282.         vertSBLen = vertHeight - rc.bottom;
  1283.         SetScrollRange(hwnd, SB_VERT, 0, (int)vertSBLen, TRUE);
  1284.             //
  1285.             // Everything fits vertically. No scrollbar necessary.
  1286.             //
  1287.     } else {
  1288.         vertSBLen = 0;
  1289.         SetScrollRange(hwnd, SB_VERT, 0, 0, TRUE);
  1290.     }
  1291.     break;
  1292.         //
  1293.         // During a wait state, show an hourglass over our client area
  1294.         //
  1295.         case WM_SETCURSOR:
  1296.             if (fWait && LOWORD(lParam) == HTCLIENT) {
  1297.                 SetCursor(LoadCursor(NULL, IDC_WAIT));
  1298.                 return TRUE;
  1299.             }
  1300.             break;
  1301. //
  1302. // We're out of here!
  1303. //
  1304.         case WM_DESTROY:
  1305.             FreeAvi(hwnd); // close all open streams
  1306.     AVIFileExit(); // shuts down the AVIFile system
  1307.     if (ghLib)
  1308.                 FreeLibrary(ghLib);
  1309.     PostQuitMessage(0);
  1310.     break;
  1311. //
  1312. // Don't let us close ourselves in a wait state (eg saving)
  1313. //
  1314.         case WM_CLOSE:
  1315.     if (fWait)
  1316. return 0;
  1317.             break;
  1318. //
  1319. // Block keyboard access to menus if waiting
  1320. //
  1321. case WM_SYSCOMMAND:
  1322.     switch (wParam & 0xFFF0) {
  1323. case SC_KEYMENU:
  1324.     if (fWait)
  1325. return 0;
  1326.     break;
  1327.     }
  1328.     break;
  1329.         case WM_PALETTECHANGED:
  1330.     // It came from us.  Ignore it
  1331.             if ((HWND)wParam == hwnd)
  1332.                 break;
  1333. case WM_QUERYNEWPALETTE:
  1334.             hdc = GetDC(hwnd);
  1335.             //
  1336.             // Realize the palette of the first video stream
  1337.             // !!! Assumes the first stream is video
  1338.             //
  1339.             if (ghdd[0] && (f = DrawDibRealize(ghdd[0], hdc, FALSE)))
  1340.                 InvalidateRect(hwnd,NULL,TRUE);
  1341.             ReleaseDC(hwnd,hdc);
  1342.             return f;
  1343.         case WM_ERASEBKGND:
  1344.             break;
  1345.         case WM_PAINT:
  1346.             hdc = BeginPaint(hwnd,&ps);
  1347.     PaintStuff(hdc, hwnd, FALSE);
  1348.             EndPaint(hwnd,&ps);
  1349.             break;
  1350. //
  1351. // handle the keyboard interface
  1352. //
  1353. case WM_KEYDOWN:
  1354.             switch (wParam)
  1355.             {
  1356.                 case VK_UP:    PostMessage (hwnd,WM_VSCROLL,SB_LINEUP,0L);   break;
  1357.                 case VK_DOWN:  PostMessage (hwnd,WM_VSCROLL,SB_LINEDOWN,0L); break;
  1358.                 case VK_PRIOR: PostMessage (hwnd,WM_HSCROLL,SB_PAGEUP,0L);   break;
  1359.                 case VK_NEXT:  PostMessage (hwnd,WM_HSCROLL,SB_PAGEDOWN,0L); break;
  1360.                 case VK_HOME:  PostMessage (hwnd,WM_HSCROLL,SB_THUMBPOSITION,0L);     break;
  1361.                 case VK_END:   PostMessage (hwnd,WM_HSCROLL,SB_THUMBPOSITION,0x7FFF); break;
  1362.                 case VK_LEFT:  PostMessage (hwnd,WM_HSCROLL,SB_LINEUP,0L);   break;
  1363.                 case VK_RIGHT: PostMessage (hwnd,WM_HSCROLL,SB_LINEDOWN,0L); break;
  1364.     }
  1365.     break;
  1366.         case WM_HSCROLL:
  1367.             l = GetScrollTime(hwnd);
  1368.             switch (GET_WM_HSCROLL_CODE(wParam, lParam)) {
  1369.                 case SB_LINEDOWN:      l += timehscroll;  break;
  1370.                 case SB_LINEUP:        l -= timehscroll;  break;
  1371.                 case SB_PAGEDOWN:      l += timeLength/10; break;
  1372.                 case SB_PAGEUP:        l -= timeLength/10; break;
  1373.                 case SB_THUMBTRACK:
  1374.                 case SB_THUMBPOSITION:
  1375. l = GET_WM_HSCROLL_POS(wParam, lParam);
  1376. l = timeStart + muldiv32(l, timeLength, SCROLLRANGE);
  1377. break;
  1378.             }
  1379.     if (l < timeStart)
  1380. l = timeStart;
  1381.     if (l > timeEnd)
  1382. l = timeEnd;
  1383.     if (l == (LONG)GetScrollTime(hwnd))
  1384. break;
  1385.     SetScrollTime(hwnd, l);
  1386.             InvalidateRect(hwnd, NULL, FALSE);
  1387.             UpdateWindow(hwnd);
  1388.             break;
  1389.         case WM_VSCROLL:
  1390.             l = GetScrollPos(hwnd, SB_VERT);
  1391.     GetClientRect(hwnd, &rc);
  1392.             switch (GET_WM_VSCROLL_CODE(wParam, lParam)) {
  1393.                 case SB_LINEDOWN:      l += 10;  break;
  1394.                 case SB_LINEUP:        l -= 10;  break;
  1395.                 case SB_PAGEDOWN:      l += rc.bottom; break;
  1396.                 case SB_PAGEUP:        l -= rc.bottom; break;
  1397.                 case SB_THUMBTRACK:
  1398.                 case SB_THUMBPOSITION:
  1399.     l = GET_WM_VSCROLL_POS(wParam, lParam);
  1400.     break;
  1401.             }
  1402.     if (l < 0)
  1403. l = 0;
  1404.     if (l > vertSBLen)
  1405. l = vertSBLen;
  1406.     if (l == GetScrollPos(hwnd, SB_VERT))
  1407. break;
  1408.     SetScrollPos(hwnd, SB_VERT, (int)l, TRUE);
  1409.             InvalidateRect(hwnd, NULL, TRUE);
  1410.             UpdateWindow(hwnd);
  1411.             break;
  1412. //
  1413. // Wave driver wants to tell us something.  Pass it on.
  1414. //
  1415. case MM_WOM_OPEN:
  1416. case MM_WOM_DONE:
  1417. case MM_WOM_CLOSE:
  1418.     aviaudioMessage(hwnd, msg, wParam, lParam);
  1419.     break;
  1420.     }
  1421.     return DefWindowProc(hwnd,msg,wParam,lParam);
  1422. }
  1423. /*----------------------------------------------------------------------------*
  1424. |    SaveCallback()
  1425. |
  1426. |    Our save callback that prints our progress in our window title bar.
  1427. *----------------------------------------------------------------------------*/
  1428. BOOL PASCAL  SaveCallback(int iProgress)
  1429. {
  1430.     TCHAR    ach[128];
  1431.     wsprintf(ach, TEXT("%s - Saving %s: %d%%"),
  1432.         (LPTSTR) gszAppName, (LPTSTR) gachSaveFileName, iProgress);
  1433.     SetWindowText(ghwndApp, ach);
  1434.     //
  1435.     // Give ourselves a chance to abort
  1436.     //
  1437.     return WinYield();
  1438. }
  1439. /*----------------------------------------------------------------------------*
  1440. |    AppCommand()
  1441. |
  1442. |    Process all of our WM_COMMAND messages.
  1443. *----------------------------------------------------------------------------*/
  1444. LONG PASCAL AppCommand (HWND hwnd, unsigned msg, WPARAM wParam, LPARAM lParam)
  1445. {
  1446.     switch(GET_WM_COMMAND_ID(wParam, lParam))
  1447.     {
  1448. //
  1449. // Our about box
  1450. //
  1451.         case MENU_ABOUT:
  1452.             DialogBox(ghInstApp, MAKEINTRESOURCE(IDD_ABOUT), hwnd, AboutDlgProc);
  1453.             break;
  1454. //
  1455. // We want out of here!
  1456. //
  1457. case MENU_EXIT:
  1458.     PostMessage(hwnd,WM_CLOSE,0,0L);
  1459.             break;
  1460. //
  1461. // Set the compression options for each stream - pass an array of
  1462. // streams and an array of compression options structures
  1463. //
  1464.         case MENU_OPTIONS:
  1465.             AVISaveOptions(hwnd, ICMF_CHOOSE_KEYFRAME | ICMF_CHOOSE_DATARATE
  1466. | ICMF_CHOOSE_PREVIEW,
  1467. gcpavi, gapavi, galpAVIOptions);
  1468.     break;
  1469.         //
  1470.         // Use palmap to set the no of colours to save with
  1471.         //
  1472.         case MENU_NEWPALETTE:
  1473.             {
  1474.        HDC       hdc;
  1475.                PAVISTREAM     psMappedStream;
  1476.                PAVISTREAM     psCurrent;
  1477.                AVISTREAMINFO  avis;
  1478.                FARPROC        lpfnAVICreateMappedStream;
  1479.        LPBITMAPINFOHEADER lpbi;
  1480. #define AVICreateMappedStream 11       //Cardinal for procedure in palmap32.dll
  1481.                lpfnAVICreateMappedStream = GetProcAddress(ghLib,MAKEINTRESOURCE(AVICreateMappedStream));
  1482.                if (lpfnAVICreateMappedStream == NULL) {
  1483.                    ErrMsg(TEXT("Unable to find AVICreateMappedStream in palmap32.dll"));
  1484.                    gbCanPalMap = FALSE;
  1485.                }
  1486.                psCurrent = gapavi[giFirstVideo];
  1487.                AVIStreamInfo(psCurrent, &avis, sizeof(avis));
  1488.                if (avis.fccType != streamtypeVIDEO) {
  1489.                    ErrMsg("Stream is not video!");
  1490.                    break;
  1491.                }
  1492.                if (DialogBoxParam(
  1493.                              ghInstApp,
  1494.                              MAKEINTRESOURCE(IDD_NCOLORS),
  1495.                              hwnd,
  1496.                              GetNumberOfColorsDlgProc,
  1497.                              (LPARAM)GetNumberOfColors(psCurrent))
  1498.                    ) {
  1499.                       StartWait();
  1500.                       //
  1501.                       //  Find out how many colours they want to map it to
  1502.                       //
  1503.                       if (0 != (*lpfnAVICreateMappedStream)(&psMappedStream,
  1504.                                               psCurrent,
  1505.                                               gnColours)) {
  1506.                            ErrMsg(TEXT("Can't map stream palette"));
  1507.                       }
  1508.                       else {
  1509.                            gapavi[giFirstVideo] = psMappedStream;
  1510.    // We've just mapped this stream to a new palette
  1511.    // so we have to re-init the GetFrame stuff used
  1512.    // to draw this stream.
  1513.    // Kick start DrawDib to realize the new palette.
  1514.    //
  1515.    if (gapgf[giFirstVideo])
  1516. AVIStreamGetFrameClose(gapgf[giFirstVideo]);
  1517.    gapgf[giFirstVideo] =
  1518. AVIStreamGetFrameOpen(gapavi[giFirstVideo], 0);
  1519.    lpbi = AVIStreamGetFrame(gapgf[giFirstVideo], 0);
  1520.    DrawDibBegin(ghdd[giFirstVideo], NULL,
  1521. lpbi->biWidth, lpbi->biHeight,
  1522. lpbi, lpbi->biWidth, lpbi->biHeight, 0);
  1523.    hdc = GetDC(hwnd);
  1524.    DrawDibRealize(ghdd[giFirstVideo], hdc, FALSE);
  1525.    ReleaseDC(hwnd, hdc);
  1526.                       }
  1527.                       EndWait();
  1528.                    }
  1529.             }
  1530.             break;
  1531. //
  1532. // Save all the open streams into a file
  1533. //
  1534.         case MENU_SAVEAS:
  1535.             {
  1536.                 OPENFILENAME ofn;
  1537.                 gachSaveFileName[0] = 0;
  1538.              //
  1539.                 // prompt user for file to save
  1540.              //
  1541.                 ofn.lStructSize = sizeof(OPENFILENAME);
  1542.                 ofn.hwndOwner = hwnd;
  1543.                 ofn.hInstance = NULL;
  1544.                 AVIBuildFilter(gachFilter, sizeof(gachFilter)/sizeof(TCHAR), TRUE);
  1545.                 ofn.lpstrFilter = gachFilter;
  1546.                 ofn.lpstrCustomFilter = NULL;
  1547.                 ofn.nMaxCustFilter = 0;
  1548.                 ofn.nFilterIndex = 0;
  1549.                 ofn.lpstrFile = gachSaveFileName;
  1550.                 ofn.nMaxFile = sizeof(gachSaveFileName)/sizeof(TCHAR);
  1551.                 ofn.lpstrFileTitle = NULL;
  1552.                 ofn.nMaxFileTitle = 0;
  1553.                 ofn.lpstrInitialDir = NULL;
  1554.                 ofn.lpstrTitle = TEXT("Save AVI File");
  1555.                 ofn.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY |
  1556.          OFN_OVERWRITEPROMPT;
  1557.                 ofn.nFileOffset = 0;
  1558.                 ofn.nFileExtension = 0;
  1559.                 ofn.lpstrDefExt = TEXT("avi");
  1560.                 ofn.lCustData = 0;
  1561.                 ofn.lpfnHook = NULL;
  1562.                 ofn.lpTemplateName = NULL;
  1563.           //
  1564.           // If we get a filename, save it
  1565.           //
  1566.                 if (GetSaveFileName(&ofn)) {
  1567.                     DWORD   fccHandler[MAXNUMSTREAMS];
  1568.              int     i;
  1569.              HRESULT hr;
  1570.         
  1571.              StartWait();
  1572.              for (i = 0; i < gcpavi; i++)
  1573.                  fccHandler[i] = galpAVIOptions[i]->fccHandler;
  1574.              hr = AVISaveV(gachSaveFileName,
  1575.                            NULL,
  1576.                            (AVISAVECALLBACK) SaveCallback,
  1577.                            gcpavi,
  1578.                            gapavi,
  1579.                            galpAVIOptions);
  1580.              if (hr != AVIERR_OK) {
  1581.                         switch (hr) {
  1582.                         case AVIERR_FILEOPEN:
  1583.                             ErrMsg(TEXT("Overwriting an open AVI file is not possible"));
  1584.                             break;
  1585.                         default:
  1586.                             ErrMsg(TEXT("Error saving AVI file"));
  1587.                         }
  1588.                     }
  1589.              // Now put the video compressors back that we stole
  1590.              for (i = 0; i < gcpavi; i++)
  1591.                   galpAVIOptions[i]->fccHandler = fccHandler[i];
  1592.         
  1593.              EndWait();
  1594.              FixWindowTitle(hwnd);
  1595.                 }
  1596.       }
  1597.     break;
  1598. //
  1599. // Close everything
  1600. //
  1601. case MENU_NEW:
  1602.     FreeAvi(hwnd);
  1603.     gachFileName[0] = TEXT('');
  1604.     FixWindowTitle(hwnd);
  1605.     break;
  1606. //
  1607. // Open a new file, or merge streams with a new file
  1608. //
  1609.         case MENU_OPEN:
  1610.         case MENU_ADD:
  1611.             {
  1612.                 OPENFILENAME ofn;
  1613.                 gachFileName[0] = 0;
  1614.              //
  1615.                 // prompt user for file to open
  1616.              //
  1617.                 ofn.lStructSize = sizeof(OPENFILENAME);
  1618.                 ofn.hwndOwner   = hwnd;
  1619.                 ofn.hInstance   = NULL;
  1620.              if (wParam == MENU_ADD)
  1621.          ofn.lpstrTitle = TEXT("Merge With");
  1622.              else
  1623.                  ofn.lpstrTitle = TEXT("Open AVI");
  1624.              if (gachFilter[0] == TEXT(''))
  1625.          AVIBuildFilter(gachFilter, sizeof(gachFilter)/sizeof(TCHAR), FALSE);
  1626.     
  1627.              ofn.lpstrFilter       = gachFilter;
  1628.                 ofn.lpstrCustomFilter = NULL;
  1629.                 ofn.nMaxCustFilter    = 0;
  1630.                 ofn.nFilterIndex      = 0;
  1631.                 ofn.lpstrFile         = gachFileName;
  1632.                 ofn.nMaxFile          = sizeof(gachFileName)/sizeof(TCHAR);
  1633.                 ofn.lpstrFileTitle    = NULL;
  1634.                 ofn.nMaxFileTitle     = 0;
  1635.                 ofn.lpstrInitialDir   = NULL;
  1636.                 ofn.Flags             = OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY;
  1637.                 ofn.nFileOffset       = 0;
  1638.                 ofn.nFileExtension    = 0;
  1639.                 ofn.lpstrDefExt       = NULL;
  1640.                 ofn.lCustData         = 0;
  1641.                 ofn.lpfnHook          = NULL;
  1642.                 ofn.lpTemplateName    = NULL;
  1643.              //
  1644.              // If we've got a filename, go open it
  1645.              //
  1646.                 if (GetOpenFileNamePreview(&ofn))
  1647.          InitAvi(hwnd, gachFileName, wParam);
  1648.             }
  1649.             break;
  1650. //
  1651. // Open the "fake" ball file as our current file
  1652. //
  1653. case MENU_BALL:
  1654.     InitBall(hwnd);
  1655.     break;
  1656. case MENU_ZOOMQUARTER:
  1657.     gwZoom = 1;
  1658.     FixScrollbars(hwnd);
  1659.             InvalidateRect(hwnd, NULL, TRUE);
  1660.     break;
  1661. case MENU_ZOOMHALF:
  1662.     gwZoom = 2;
  1663.     FixScrollbars(hwnd);
  1664.             InvalidateRect(hwnd, NULL, TRUE);
  1665.     break;
  1666. case MENU_ZOOM1:
  1667.     gwZoom = 4;
  1668.     FixScrollbars(hwnd);
  1669.             InvalidateRect(hwnd, NULL, TRUE);
  1670.     break;
  1671. case MENU_ZOOM2:
  1672.     gwZoom = 8;
  1673.     FixScrollbars(hwnd);
  1674.             InvalidateRect(hwnd, NULL, TRUE);
  1675.     break;
  1676. case MENU_ZOOM4:
  1677.     gwZoom = 16;
  1678.     FixScrollbars(hwnd);
  1679.             InvalidateRect(hwnd, NULL, TRUE);
  1680.             break;
  1681. //
  1682. // Simulate playing the file.  We just play the 1st audio stream and let
  1683. // our main message loop scroll the video by whenever it's bored.
  1684. //
  1685. case MENU_PLAY:
  1686.     if (gfAudioFound)
  1687.         aviaudioPlay(hwnd,
  1688.  gpaviAudio,
  1689.  AVIStreamTimeToSample(gpaviAudio, GetScrollTime(hwnd)),
  1690.  AVIStreamEnd(gpaviAudio),
  1691.  FALSE);
  1692.             gfPlaying = TRUE;
  1693.             glPlayStartTime = timeGetTime();
  1694.             glPlayStartPos = GetScrollTime(hwnd);
  1695.             break;
  1696. //
  1697. // Stop the play preview
  1698. //
  1699. case MENU_STOP:
  1700.     if (gfAudioFound)
  1701.         aviaudioStop();
  1702.     gfPlaying = FALSE;
  1703.     break;
  1704.     }
  1705.     return 0L;
  1706. }
  1707. /*----------------------------------------------------------------------------*
  1708. |   ErrMsg - Opens a Message box with a error message in it.  The user can     |
  1709. |            select the OK button to continue                                  |
  1710. *----------------------------------------------------------------------------*/
  1711. int ErrMsg (LPTSTR sz,...)
  1712. {
  1713.     static TCHAR ach[2000];
  1714.     va_list va;
  1715.     va_start(va, sz);
  1716.     wvsprintf (ach,sz, va);
  1717.     va_end(va);
  1718.     MessageBox(NULL,ach,NULL,
  1719. #ifdef BIDI
  1720. MB_RTL_READING |
  1721. #endif
  1722.     MB_OK|MB_ICONEXCLAMATION|MB_TASKMODAL);
  1723.     return FALSE;
  1724. }
  1725. // Find the number of colours present in the stream palette
  1726. LONG GetNumberOfColors(PAVISTREAM ps)
  1727. {
  1728.     BITMAPINFOHEADER bmih;
  1729.     LONG             cbFormat = sizeof(bmih);
  1730.     bmih.biClrUsed = 0;
  1731.     AVIStreamReadFormat(ps, 0, (LPVOID)&bmih, &cbFormat);
  1732.     return (LONG)bmih.biClrUsed;
  1733. }
  1734. /* GetNumberOfColorsDlgProc()
  1735.  *
  1736.  * Dialog Procedure for getting the no of colours the user wants in the palette
  1737.  *
  1738.  */
  1739. BOOL CALLBACK GetNumberOfColorsDlgProc(
  1740. HWND hwnd,
  1741. UINT msg,
  1742. WPARAM wParam,
  1743. LPARAM lParam)
  1744. {
  1745.         BOOL Translated;
  1746. switch (msg) {
  1747. case WM_COMMAND:
  1748.             switch (GET_WM_COMMAND_ID(wParam, lParam)) {
  1749.             case IDOK:
  1750. gnColours = GetDlgItemInt(hwnd, IDC_NCOLORS, &Translated, FALSE);
  1751.                 EndDialog(hwnd, TRUE);
  1752.                 return TRUE;
  1753.             case IDCANCEL:
  1754.                 EndDialog(hwnd, FALSE);
  1755.             }
  1756.             break;
  1757. case WM_INITDIALOG:
  1758.             {
  1759.                 SetDlgItemInt(hwnd, IDC_NCOLORS, lParam, FALSE);
  1760. return TRUE;
  1761.             }
  1762. }
  1763. return FALSE;
  1764. }
  1765. /* AboutDlgProc()
  1766.  *
  1767.  * Dialog Procedure for the "about" dialog box.
  1768.  *
  1769.  */
  1770. BOOL CALLBACK AboutDlgProc(
  1771. HWND hwnd,
  1772. UINT msg,
  1773. WPARAM wParam,
  1774. LPARAM lParam)
  1775. {
  1776. switch (msg) {
  1777. case WM_COMMAND:
  1778. EndDialog(hwnd, TRUE);
  1779. return TRUE;
  1780.         case WM_INITDIALOG:
  1781.                 if (gbCanPalMap)                                         // Provide the user with
  1782.                     ShowWindow(GetDlgItem(hwnd, IDS_PALETTE), SW_HIDE);  // some information about
  1783.                 else                                                     // whether palmap32.dll was
  1784.                     ShowWindow(GetDlgItem(hwnd, IDS_PALETTE), SW_SHOW);  // found.
  1785.                 return TRUE;
  1786. }
  1787. return FALSE;
  1788. }