Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1988 lines
57 KiB

  1. #include <windows.h>
  2. #include <windowsx.h>
  3. #include <win32.h>
  4. #include <commdlg.h>
  5. #include <vfw.h>
  6. #define CLIPSTUFF
  7. #ifdef CLIPSTUFF
  8. #include <vfw.h>
  9. #endif
  10. #include <msacm.h>
  11. #define TEST_FINDSAMPLE
  12. #ifndef streamtypeTEXT
  13. #pragma message("streamtypeTEXT is not defined in AVIFMT.H")
  14. #define streamtypeTEXT mmioFOURCC('t', 'x', 't', 's')
  15. #endif
  16. #include <memory.h>
  17. #include "aviview.h"
  18. #include "audplay.h"
  19. #define GlobalSizePtr(lp) GlobalSize(GlobalPtrHandle(lp))
  20. #ifndef WIN32
  21. extern LONG FAR PASCAL muldiv32(LONG, LONG, LONG);
  22. #endif
  23. extern BOOL RegisterObjects(void);
  24. extern void RevokeObjects(void);
  25. TCHAR gachFilter[512] = TEXT("");
  26. #define FIXCC(fcc) if (fcc == 0) fcc = mmioFOURCC('N', 'o', 'n', 'e'); \
  27. if (fcc == BI_RLE8) fcc = mmioFOURCC('R', 'l', 'e', '8');
  28. /*----------------------------------------------------------------------------*\
  29. \*----------------------------------------------------------------------------*/
  30. typedef LONG (FAR PASCAL *LPWNDPROC)(HWND, UINT, WPARAM, LPARAM); // pointer to a window procedure
  31. /*----------------------------------------------------------------------------*\
  32. \*----------------------------------------------------------------------------*/
  33. static TCHAR gszAppName[]=TEXT("AVIView");
  34. static HANDLE ghInstApp;
  35. static HWND ghwndApp;
  36. static HACCEL ghAccel;
  37. #define SCROLLRANGE 10000
  38. #define MAXNUMSTREAMS 50
  39. int giCurrentStream; // current stream;
  40. PAVIFILE gpfile; // the current file
  41. int gaiStreamTop[MAXNUMSTREAMS];
  42. PAVISTREAM gapavi[MAXNUMSTREAMS]; // the current streams
  43. PGETFRAME gapgf[MAXNUMSTREAMS]; // data for decompressing
  44. // video
  45. HDRAWDIB ghdd[MAXNUMSTREAMS]; // drawdib handles
  46. HIC ghic[MAXNUMSTREAMS]; // experimental: installable
  47. // draw handlers for non-video
  48. // streams
  49. int gcpavi; // # of streams
  50. BOOL gfPlaying = FALSE; // Are we playing right now?
  51. LONG glPlayStartTime; // When did we start playing?
  52. LONG glPlayStartPos; // From what position?
  53. PAVISTREAM gpaviAudio; // 1st audio stream found
  54. PAVISTREAM gpaviVideo; // 1st video stream found
  55. #define gfVideoFound (gpaviVideo != NULL)
  56. #define gfAudioFound (gpaviAudio != NULL)
  57. LONG timeStart; // cached start, end, length
  58. LONG timeEnd;
  59. LONG timeLength;
  60. LONG timehscroll; // how much arrows scroll HORZ bar
  61. LONG vertSBLen;
  62. LONG vertHeight;
  63. DWORD gdwMicroSecPerPixel = 1000L; // !!! x-stretch
  64. TCHAR gachFileName[MAX_PATH] = TEXT("");
  65. TCHAR gachSaveFileName[MAX_PATH] = TEXT("");
  66. UINT gwZoom = 2;
  67. AVICOMPRESSOPTIONS gaAVIOptions[MAXNUMSTREAMS];
  68. LPAVICOMPRESSOPTIONS galpAVIOptions[MAXNUMSTREAMS];
  69. HFONT hfontApp;
  70. TEXTMETRIC tm;
  71. // !!! constants for painting
  72. #define VSPACE 8 // no one will ever know what this means :-(
  73. #define HSPACE 4 // space between frames
  74. #define TSPACE (tm.tmHeight) // space for text area about each stream
  75. #define AUDIOVSPACE 64 // height of an audio stream
  76. #define FRAME_BORDER 2
  77. void SaveSmall(PAVISTREAM ps, LPTSTR lpFilename);
  78. /*----------------------------------------------------------------------------*\
  79. \*----------------------------------------------------------------------------*/
  80. #define GetScrollTime(hwnd) \
  81. (timeStart + muldiv32(GetScrollPos(hwnd, SB_HORZ), timeLength, SCROLLRANGE))
  82. #define SetScrollTime(hwnd, time) SetScrollPos(hwnd, SB_HORZ, \
  83. (int)muldiv32((time) - timeStart, SCROLLRANGE, timeLength), TRUE)
  84. /*----------------------------------------------------------------------------*\
  85. \*----------------------------------------------------------------------------*/
  86. LONG FAR PASCAL _export AppWndProc (HWND hwnd, unsigned uiMessage, WPARAM wParam, LPARAM lParam);
  87. int ErrMsg (LPTSTR sz,...);
  88. BOOL fDialog(int id,HWND hwnd,FARPROC fpfn);
  89. LONG NEAR PASCAL AppCommand(HWND hwnd, unsigned msg, WPARAM wParam, LPARAM lParam);
  90. /*----------------------------------------------------------------------------*\
  91. \*----------------------------------------------------------------------------*/
  92. HCURSOR hcurSave;
  93. int fWait = 0;
  94. void StartWait()
  95. {
  96. if (fWait++ == 0)
  97. {
  98. SetCursor(LoadCursor(NULL,IDC_WAIT));
  99. }
  100. }
  101. void EndWait()
  102. {
  103. if (--fWait == 0)
  104. {
  105. SetCursor(LoadCursor(NULL,IDC_ARROW));
  106. InvalidateRect(ghwndApp, NULL, TRUE);
  107. }
  108. }
  109. BOOL WinYield()
  110. {
  111. MSG msg;
  112. BOOL fAbort=FALSE;
  113. while(fWait > 0 && PeekMessage(&msg,NULL,0,0,PM_REMOVE))
  114. {
  115. if (msg.message == WM_KEYDOWN && msg.wParam == VK_ESCAPE)
  116. fAbort = TRUE;
  117. if (msg.message == WM_SYSCOMMAND && (msg.wParam & 0xFFF0) == SC_CLOSE)
  118. fAbort = TRUE;
  119. TranslateMessage(&msg);
  120. DispatchMessage(&msg);
  121. }
  122. return fAbort;
  123. }
  124. /*----------------------------------------------------------------------------*\
  125. \*----------------------------------------------------------------------------*/
  126. //
  127. // When we load a file or zoom changes, we re-set the scrollbars
  128. //
  129. void FixScrollbars(HWND hwnd)
  130. {
  131. AVISTREAMINFO avis;
  132. LONG r, lHeight = 0;
  133. UINT w;
  134. int i;
  135. RECT rc;
  136. //
  137. // Walk through all streams and determine how many pixels it will take to
  138. // draw it.
  139. //
  140. for (i = 0; i < gcpavi; i++) {
  141. AVIStreamInfo(gapavi[i], &avis, sizeof(avis));
  142. if (avis.fccType == streamtypeVIDEO) {
  143. //
  144. // Set the horizontal scrollbar scale to show every frame
  145. // of the first video stream exactly once
  146. //
  147. if (gapavi[i] == gpaviVideo) {
  148. w = (avis.rcFrame.right - avis.rcFrame.left) * gwZoom / 4 +
  149. HSPACE;
  150. r = (LONG)(avis.dwRate / avis.dwScale);
  151. gdwMicroSecPerPixel = muldiv32(1000000, 1, w * r);
  152. timehscroll = 1000 / r; // msec per frame
  153. }
  154. lHeight += TSPACE + TSPACE / 2 + TSPACE +
  155. (avis.rcFrame.bottom - avis.rcFrame.top) * gwZoom / 4;
  156. } else if (avis.fccType == streamtypeAUDIO) {
  157. lHeight += TSPACE + AUDIOVSPACE * gwZoom / 4;
  158. } else if (avis.fccType == streamtypeTEXT) {
  159. lHeight += TSPACE + TSPACE;
  160. ghic[i] = ICDrawOpen(avis.fccType, avis.fccHandler, NULL);
  161. }
  162. //
  163. // Every stream has this much space
  164. //
  165. lHeight += TSPACE + TSPACE + TSPACE + VSPACE;
  166. }
  167. //
  168. // Set vertical scrollbar for scrolling the visible area
  169. //
  170. GetClientRect(hwnd, &rc);
  171. vertHeight = lHeight; // total height in pixels of entire display
  172. //
  173. // We won't fit in the window... need scrollbars
  174. //
  175. if (lHeight > rc.bottom) {
  176. vertSBLen = lHeight - rc.bottom;
  177. SetScrollRange(hwnd, SB_VERT, 0, (int)vertSBLen, TRUE);
  178. SetScrollPos(hwnd, SB_VERT, 0, TRUE);
  179. //
  180. // We will fit in the window! No scrollbars necessary
  181. //
  182. } else {
  183. vertSBLen = 0;
  184. SetScrollRange(hwnd, SB_VERT, 0, 0, TRUE);
  185. }
  186. }
  187. //
  188. // Initialize the streams of a loaded file -- the compression options, the
  189. // DrawDIB handles, and the scroll bars
  190. //
  191. void InitStreams(HWND hwnd)
  192. {
  193. AVISTREAMINFO avis;
  194. LONG lTemp;
  195. int i;
  196. //
  197. // Start with bogus times
  198. //
  199. timeStart = 0x7FFFFFFF;
  200. timeEnd = 0;
  201. //
  202. // Walk through and init all streams loaded
  203. //
  204. for (i = 0; i < gcpavi; i++) {
  205. AVIStreamInfo(gapavi[i], &avis, sizeof(avis));
  206. //
  207. // Save and SaveOptions code takes a pointer to our compression opts
  208. //
  209. galpAVIOptions[i] = &gaAVIOptions[i];
  210. //
  211. // clear options structure to zeroes
  212. //
  213. _fmemset(galpAVIOptions[i], 0, sizeof(AVICOMPRESSOPTIONS));
  214. //
  215. // Initialize the compression options to some default stuff
  216. // !!! Pick something better
  217. //
  218. galpAVIOptions[i]->fccType = avis.fccType;
  219. switch(avis.fccType) {
  220. case streamtypeVIDEO:
  221. galpAVIOptions[i]->dwFlags = AVICOMPRESSF_VALID |
  222. AVICOMPRESSF_KEYFRAMES | AVICOMPRESSF_DATARATE;
  223. galpAVIOptions[i]->fccHandler = 0;
  224. galpAVIOptions[i]->dwQuality = (DWORD)ICQUALITY_DEFAULT;
  225. galpAVIOptions[i]->dwKeyFrameEvery = 7; // !!! ask compressor?
  226. galpAVIOptions[i]->dwBytesPerSecond = 60000;
  227. break;
  228. case streamtypeAUDIO:
  229. galpAVIOptions[i]->dwFlags |= AVICOMPRESSF_VALID;
  230. galpAVIOptions[i]->dwInterleaveEvery = 5;
  231. acmMetrics(NULL,
  232. ACM_METRIC_MAX_SIZE_FORMAT,
  233. (LPVOID) &galpAVIOptions[i]->cbFormat);
  234. galpAVIOptions[i]->lpFormat =
  235. GlobalAllocPtr(GHND, galpAVIOptions[i]->cbFormat);
  236. lTemp = galpAVIOptions[i]->cbFormat;
  237. // Use current format as default format
  238. AVIStreamReadFormat(gapavi[i], 0,
  239. galpAVIOptions[i]->lpFormat,
  240. &lTemp);
  241. break;
  242. default:
  243. break;
  244. }
  245. //
  246. // We're finding the earliest and latest start and end points for
  247. // our scrollbar.
  248. //
  249. timeStart = min(timeStart, AVIStreamStartTime(gapavi[i]));
  250. timeEnd = max(timeEnd, AVIStreamEndTime(gapavi[i]));
  251. //
  252. // Initialize video streams for getting decompressed frames to display
  253. //
  254. if (avis.fccType == streamtypeVIDEO) {
  255. #if 1
  256. gapgf[i] = AVIStreamGetFrameOpen(gapavi[i], NULL);
  257. #else
  258. // Alternate code for testing AVIStreamGetFrameOpen
  259. BITMAPINFOHEADER bih;
  260. bih.biSize = sizeof(bih);
  261. bih.biClrUsed = 256;
  262. bih.biBitCount = 8;
  263. bih.biPlanes = 1;
  264. bih.biWidth = 0;
  265. bih.biHeight = 0;
  266. bih.biCompression = BI_RGB;
  267. bih.biSizeImage = 0;
  268. bih.biClrImportant = 0;
  269. gapgf[i] = AVIStreamGetFrameOpen(gapavi[i], &bih);
  270. #endif
  271. if (gapgf[i] == NULL)
  272. continue;
  273. ghdd[i] = DrawDibOpen();
  274. // !!! DrawDibBegin?
  275. if (gpaviVideo == NULL) {
  276. //
  277. // Remember the first video stream --- treat it specially
  278. //
  279. gpaviVideo = gapavi[i];
  280. }
  281. } else if (avis.fccType == streamtypeAUDIO) {
  282. //
  283. // Remember the first audio stream --- treat it specially
  284. //
  285. if (gpaviAudio == NULL)
  286. gpaviAudio = gapavi[i];
  287. }
  288. }
  289. timeLength = timeEnd - timeStart;
  290. SetScrollRange(hwnd, SB_HORZ, 0, SCROLLRANGE, TRUE);
  291. SetScrollTime(hwnd, timeStart);
  292. FixScrollbars(hwnd);
  293. }
  294. //
  295. // Update the window title to reflect what's loaded
  296. //
  297. void FixWindowTitle(HWND hwnd)
  298. {
  299. TCHAR ach[80];
  300. wsprintf(ach, TEXT("%s %s"),
  301. (LPTSTR)gszAppName,
  302. (LPTSTR)gachFileName);
  303. SetWindowText(hwnd, ach);
  304. InvalidateRect(hwnd, NULL, TRUE);
  305. }
  306. void FreeDrawStuff(HWND hwnd)
  307. {
  308. int i;
  309. aviaudioStop();
  310. for (i = 0; i < gcpavi; i++) {
  311. if (gapgf[i]) {
  312. AVIStreamGetFrameClose(gapgf[i]);
  313. gapgf[i] = NULL;
  314. }
  315. if (ghdd[i]) {
  316. DrawDibClose(ghdd[i]);
  317. ghdd[i] = 0;
  318. }
  319. if (ghic[i]) {
  320. ICClose(ghic[i]);
  321. ghic[i] = 0;
  322. }
  323. }
  324. SetScrollRange(hwnd, SB_HORZ, 0, 0, TRUE);
  325. gpaviVideo = gpaviAudio = NULL;
  326. }
  327. void FreeAvi(HWND hwnd)
  328. {
  329. int i;
  330. FreeDrawStuff(hwnd);
  331. for (i = 0; i < gcpavi; i++) {
  332. AVIStreamClose(gapavi[i]);
  333. if (galpAVIOptions[i]->lpFormat) {
  334. GlobalFreePtr(galpAVIOptions[i]->lpFormat);
  335. }
  336. }
  337. if (gpfile)
  338. AVIFileClose(gpfile);
  339. gpfile = NULL;
  340. gcpavi = 0;
  341. giCurrentStream = 0;
  342. }
  343. void InitBall(HWND hwnd)
  344. {
  345. PAVISTREAM FAR PASCAL NewBall(void);
  346. FreeAvi(hwnd);
  347. gapavi[0] = NewBall();
  348. if (gapavi[0])
  349. gcpavi = 1;
  350. lstrcpy(gachFileName, TEXT("BALL"));
  351. InitStreams(hwnd);
  352. FixWindowTitle(hwnd);
  353. }
  354. void InsertAVIFile(PAVIFILE pfile, HWND hwnd, LPTSTR lpszFile)
  355. {
  356. int i;
  357. for (i = gcpavi; i < MAXNUMSTREAMS; i++) {
  358. gapavi[i] = NULL;
  359. if (AVIFileGetStream(pfile, &gapavi[i], 0L, i - gcpavi) != AVIERR_OK)
  360. break;
  361. if (gapavi[i] == NULL)
  362. break;
  363. }
  364. if (gcpavi == i)
  365. {
  366. ErrMsg(TEXT("Unable to open %s"), lpszFile);
  367. if (pfile)
  368. AVIFileClose(pfile);
  369. goto exit;
  370. }
  371. gcpavi = i;
  372. if (gpfile) {
  373. AVIFileClose(pfile);
  374. } else
  375. gpfile = pfile;
  376. exit:
  377. InitStreams(hwnd);
  378. FixWindowTitle(hwnd);
  379. }
  380. void InitAvi(HWND hwnd, LPTSTR szFile, UINT wMenu)
  381. {
  382. HRESULT hr;
  383. PAVIFILE pfile;
  384. hr = AVIFileOpen(&pfile, szFile, OF_SHARE_DENY_WRITE, 0L);
  385. if (hr != 0)
  386. {
  387. ErrMsg(TEXT("Unable to open %s"), szFile);
  388. return;
  389. }
  390. if (wMenu == MENU_OPEN)
  391. FreeAvi(hwnd);
  392. else
  393. FreeDrawStuff(hwnd);
  394. InsertAVIFile(pfile, hwnd, szFile);
  395. }
  396. /*----------------------------------------------------------------------------*\
  397. | AppInit( hInst, hPrev) |
  398. | |
  399. | Description: |
  400. | This is called when the application is first loaded into |
  401. | memory. It performs all initialization that doesn't need to be done |
  402. | once per instance. |
  403. | |
  404. | Arguments: |
  405. | hInstance instance handle of current instance |
  406. | hPrev instance handle of previous instance |
  407. | |
  408. | Returns: |
  409. | TRUE if successful, FALSE if not |
  410. | |
  411. \*----------------------------------------------------------------------------*/
  412. BOOL AppInit(HINSTANCE hInst, HINSTANCE hPrev, int sw,LPSTR szCmdLine)
  413. {
  414. WNDCLASS cls;
  415. int dx,dy;
  416. /* Save instance handle for DialogBoxs */
  417. ghInstApp = hInst;
  418. ghAccel = LoadAccelerators(hInst, MAKEINTATOM(ID_APP));
  419. if (szCmdLine && szCmdLine[0]) {
  420. #ifdef UNICODE
  421. // convert to unicode
  422. lstrcpy(gachFileName, GetCommandLine());
  423. #else
  424. lstrcpy(gachFileName, szCmdLine);
  425. #endif
  426. }
  427. if (!hPrev) {
  428. /*
  429. * Register a class for the main application window
  430. */
  431. cls.hCursor = LoadCursor(NULL,IDC_ARROW);
  432. cls.hIcon = LoadIcon(hInst,MAKEINTATOM(ID_APP));
  433. cls.lpszMenuName = MAKEINTATOM(ID_APP);
  434. cls.lpszClassName = MAKEINTATOM(ID_APP);
  435. cls.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
  436. cls.hInstance = hInst;
  437. cls.style = CS_BYTEALIGNCLIENT | CS_VREDRAW | CS_HREDRAW | CS_DBLCLKS;
  438. cls.lpfnWndProc = (LPWNDPROC)AppWndProc;
  439. cls.cbWndExtra = 0;
  440. cls.cbClsExtra = 0;
  441. if (!RegisterClass(&cls))
  442. return FALSE;
  443. }
  444. AVIStreamInit();
  445. RegisterObjects();
  446. {HDC hdc;
  447. hfontApp = GetStockObject(ANSI_VAR_FONT);
  448. hdc = GetDC(NULL);
  449. SelectObject(hdc, hfontApp);
  450. GetTextMetrics(hdc, &tm);
  451. ReleaseDC(NULL, hdc);
  452. }
  453. dx = GetSystemMetrics (SM_CXSCREEN);
  454. dy = GetSystemMetrics (SM_CYSCREEN);
  455. ghwndApp =
  456. CreateWindowEx(
  457. #ifdef BIDI
  458. WS_EX_BIDI_SCROLL | WS_EX_BIDI_MENU |WS_EX_BIDI_NOICON,
  459. #else
  460. 0,
  461. #endif
  462. MAKEINTATOM(ID_APP), // Class name
  463. gszAppName, // Caption
  464. WS_OVERLAPPEDWINDOW, // Style bits
  465. CW_USEDEFAULT, 0, // Position
  466. 320,300, // Size
  467. (HWND)NULL, // Parent window (no parent)
  468. (HMENU)NULL, // use class menu
  469. (HANDLE)hInst, // handle to window instance
  470. (LPSTR)NULL // no params to pass on
  471. );
  472. ShowWindow(ghwndApp,sw);
  473. return TRUE;
  474. }
  475. /*----------------------------------------------------------------------------*\
  476. | WinMain( hInst, hPrev, lpszCmdLine, cmdShow ) |
  477. | |
  478. | Description: |
  479. | The main procedure for the App. After initializing, it just goes |
  480. | into a message-processing loop until it gets a WM_QUIT message |
  481. | (meaning the app was closed). |
  482. | |
  483. | Arguments: |
  484. | hInst instance handle of this instance of the app |
  485. | hPrev instance handle of previous instance, NULL if first |
  486. | szCmdLine ->null-terminated command line |
  487. | cmdShow specifies how the window is initially displayed |
  488. | |
  489. | Returns: |
  490. | The exit code as specified in the WM_QUIT message. |
  491. | |
  492. \*----------------------------------------------------------------------------*/
  493. int PASCAL WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR szCmdLine, int sw)
  494. {
  495. MSG msg;
  496. /* Call initialization procedure */
  497. if (!AppInit(hInst,hPrev,sw,szCmdLine))
  498. return FALSE;
  499. /*
  500. * Polling messages from event queue
  501. */
  502. for (;;)
  503. {
  504. while (PeekMessage(&msg, NULL, 0, 0,PM_REMOVE))
  505. {
  506. if (msg.message == WM_QUIT)
  507. return msg.wParam;
  508. if (TranslateAccelerator(ghwndApp, ghAccel, &msg))
  509. continue;
  510. TranslateMessage(&msg);
  511. DispatchMessage(&msg);
  512. }
  513. //
  514. // If we have no messages to dispatch, we do our background task...
  515. // If we're playing a file, we set the scroll bar to show the video
  516. // frames corresponding with the current playing audio sample
  517. //
  518. if (gfPlaying) {
  519. LONG l;
  520. //
  521. // Use the audio clock to tell how long we've been playing. To
  522. // maintain sync, it's important we use this clock.
  523. //
  524. l = aviaudioTime(); // returns -1 if no audio playing
  525. //
  526. // If we can't use the audio clock to tell us how long we've been
  527. // playing, calculate it ourself
  528. //
  529. if (l == -1)
  530. l = timeGetTime() - glPlayStartTime + glPlayStartPos;
  531. if (l != GetScrollTime(ghwndApp)) {
  532. if (l < timeStart) // make sure number isn't out of bounds
  533. l = timeStart;
  534. if (l > timeEnd) // looks like we're all done!
  535. gfPlaying = FALSE;
  536. SetScrollTime(ghwndApp, l);
  537. InvalidateRect(ghwndApp, NULL, FALSE);
  538. UpdateWindow(ghwndApp);
  539. continue;
  540. }
  541. }
  542. WaitMessage();
  543. }
  544. return msg.wParam;
  545. }
  546. typedef BYTE _huge * HPBYTE;
  547. typedef int _huge * HPINT;
  548. void PaintAudio(HDC hdc, PRECT prc, PAVISTREAM pavi, LONG lStart, LONG lLen)
  549. {
  550. PCMWAVEFORMAT wf;
  551. int i;
  552. int x,y;
  553. int w,h;
  554. BYTE b;
  555. HBRUSH hbr;
  556. RECT rc = *prc;
  557. LONG lBytes;
  558. LONG l, lLenOrig = lLen;
  559. LONG lWaveBeginTime = AVIStreamStartTime(pavi);
  560. LONG lWaveEndTime = AVIStreamEndTime(pavi);
  561. static LPVOID lpAudio = NULL;
  562. // We've been told to draw some times that don't exist
  563. if (lStart < lWaveBeginTime) {
  564. lLen -= lWaveBeginTime - lStart;
  565. lStart = lWaveBeginTime;
  566. rc.left = rc.right - (int)muldiv32(rc.right - rc.left, lLen, lLenOrig);
  567. }
  568. if (lStart + lLen > lWaveEndTime) {
  569. lLenOrig = lLen;
  570. lLen = lWaveEndTime - lStart;
  571. rc.right = rc.left + (int)muldiv32(rc.right - rc.left, lLen, lLenOrig);
  572. }
  573. /* Now change and work with samples, not time. */
  574. l = lStart;
  575. lStart = AVIStreamTimeToSample(pavi, lStart);
  576. lLen = AVIStreamTimeToSample(pavi, l + lLen) - lStart;
  577. l = sizeof(wf);
  578. AVIStreamReadFormat(pavi, lStart, &wf, &l);
  579. if (!l)
  580. return;
  581. w = rc.right - rc.left;
  582. h = rc.bottom - rc.top;
  583. if (rc.left > prc->left) {
  584. SelectObject(hdc, GetStockObject(DKGRAY_BRUSH));
  585. PatBlt(hdc, prc->left, rc.top, rc.left - prc->left,
  586. rc.bottom - rc.top, PATCOPY);
  587. }
  588. if (rc.right < prc->right) {
  589. SelectObject(hdc, GetStockObject(DKGRAY_BRUSH));
  590. PatBlt(hdc, rc.right, rc.top, prc->right - rc.right,
  591. rc.bottom - rc.top, PATCOPY);
  592. }
  593. #ifdef WIN32
  594. #define BACKBRUSH (RGB(0, 0, 0))
  595. #define MONOBRUSH (RGB(0,255,0))
  596. #define LEFTBRUSH (RGB(0,0,255))
  597. #define RIGHTBRUSH (RGB(0,255,0))
  598. #define HPOSBRUSH (RGB(255,0,0))
  599. #else
  600. #define BACKBRUSH (GetSysColor(COLOR_3DFACE))
  601. #define MONOBRUSH (GetSysColor(COLOR_3DSHADOW))
  602. #define LEFTBRUSH (RGB(0,0,255))
  603. #define RIGHTBRUSH (RGB(0,255,0))
  604. #define HPOSBRUSH (RGB(255,0,0))
  605. #endif
  606. hbr = SelectObject(hdc, CreateSolidBrush(BACKBRUSH));
  607. PatBlt(hdc, rc.left, rc.top, w, h, PATCOPY);
  608. DeleteObject(SelectObject(hdc, hbr));
  609. //
  610. // !!! we can only paint PCM data
  611. //
  612. if (wf.wf.wFormatTag != WAVE_FORMAT_PCM)
  613. return;
  614. lBytes = lLen * wf.wf.nChannels * wf.wBitsPerSample / 8;
  615. if (!lpAudio)
  616. lpAudio = GlobalAllocPtr (GHND, lBytes);
  617. else if ((LONG)GlobalSizePtr(lpAudio) < lBytes)
  618. lpAudio = GlobalReAllocPtr(lpAudio, lBytes, GMEM_MOVEABLE);
  619. if (!lpAudio)
  620. return;
  621. AVIStreamRead(pavi, lStart, lLen, lpAudio, lBytes, NULL, &l);
  622. if (l != lLen)
  623. ; // FatalAppExit(0, "BLAH!!!");
  624. lLen = l;
  625. if (lLen == 0)
  626. return;
  627. #define MulDiv(a,b,c) (UINT)((DWORD)(UINT)(a) * (DWORD)(UINT)(b) / (UINT)(c))
  628. // !!! Flickers less painting it NOW or LATER?
  629. // First show the current position as a bar
  630. hbr = SelectObject(hdc, CreateSolidBrush(HPOSBRUSH));
  631. PatBlt(hdc, prc->right / 2, prc->top, 1, prc->bottom - prc->top, PATCOPY);
  632. DeleteObject(SelectObject(hdc, hbr));
  633. // Mono
  634. if (wf.wf.nChannels == 1) {
  635. hbr = SelectObject(hdc, CreateSolidBrush(MONOBRUSH));
  636. y = rc.top + h/2;
  637. PatBlt(hdc, rc.left, y, w, 1, PATCOPY);
  638. if (wf.wBitsPerSample == 8) {
  639. for (x=0; x<w; x++) {
  640. b = *((HPBYTE)lpAudio + muldiv32(x,lLen,w));
  641. if (b > 0x80) {
  642. i = y - MulDiv(b-0x80,(h/2),128);
  643. PatBlt(hdc, rc.left+x, i, 1, y-i, PATCOPY);
  644. }
  645. else {
  646. i = y + MulDiv(0x80-b,(h/2),128);
  647. PatBlt(hdc, rc.left+x, y, 1, i-y, PATCOPY);
  648. }
  649. }
  650. }
  651. else if (wf.wBitsPerSample == 16) {
  652. for (x=0; x<w; x++) {
  653. i = *((HPINT)lpAudio + muldiv32(x,lLen,w));
  654. if (i > 0) {
  655. i = y - (int) ((LONG)i * (h/2) / 32768);
  656. PatBlt(hdc, rc.left+x, i, 1, y-i, PATCOPY);
  657. }
  658. else {
  659. i = (int) ((LONG)i * (h/2) / 32768);
  660. PatBlt(hdc, rc.left+x, y, 1, -i, PATCOPY);
  661. }
  662. }
  663. }
  664. DeleteObject(SelectObject(hdc, hbr));
  665. } // endif mono
  666. // Stereo
  667. else if (wf.wf.nChannels == 2) {
  668. if (wf.wBitsPerSample == 8) {
  669. // Left channel
  670. hbr = SelectObject(hdc, CreateSolidBrush(LEFTBRUSH));
  671. y = rc.top + h/4;
  672. PatBlt(hdc, rc.left, y, w, 1, PATCOPY);
  673. for (x=0; x<w; x++) {
  674. b = *((HPBYTE)lpAudio + muldiv32(x,lLen,w) * 2);
  675. if (b > 0x80) {
  676. i = y - MulDiv(b-0x80,(h/4),128);
  677. PatBlt(hdc, rc.left+x, i, 1, y-i, PATCOPY);
  678. }
  679. else {
  680. i = y + MulDiv(0x80-b,(h/4),128);
  681. PatBlt(hdc, rc.left+x, y, 1, i-y, PATCOPY);
  682. }
  683. }
  684. // Right channel
  685. DeleteObject(SelectObject(hdc, hbr));
  686. hbr = SelectObject(hdc, CreateSolidBrush(RIGHTBRUSH));
  687. y = rc.top + h * 3 / 4;
  688. PatBlt(hdc, rc.left, y, w, 1, PATCOPY);
  689. for (x=0; x<w; x++) {
  690. b = *((HPBYTE)lpAudio + muldiv32(x,lLen,w) * 2 + 1);
  691. if (b > 0x80) {
  692. i = y - MulDiv(b-0x80,(h/4),128);
  693. PatBlt(hdc, rc.left+x, i, 1, y-i, PATCOPY);
  694. }
  695. else {
  696. i = y + MulDiv(0x80-b,(h/4),128);
  697. PatBlt(hdc, rc.left+x, y, 1, i-y, PATCOPY);
  698. }
  699. }
  700. DeleteObject(SelectObject(hdc, hbr));
  701. }
  702. else if (wf.wBitsPerSample == 16) {
  703. // Left channel
  704. hbr = SelectObject(hdc, CreateSolidBrush(LEFTBRUSH));
  705. y = rc.top + h/4;
  706. PatBlt(hdc, rc.left, y, w, 1, PATCOPY);
  707. for (x=0; x<w; x++) {
  708. i = *((HPINT)lpAudio + muldiv32(x,lLen,w) * 2);
  709. if (i > 0) {
  710. i = y - (int) ((LONG)i * (h/4) / 32768);
  711. PatBlt(hdc, rc.left+x, i, 1, y-i, PATCOPY);
  712. }
  713. else {
  714. i = (int) ((LONG)i * (h/4) / 32768);
  715. PatBlt(hdc, rc.left+x, y, 1, -i, PATCOPY);
  716. }
  717. }
  718. // Right channel
  719. DeleteObject(SelectObject(hdc, hbr));
  720. hbr = SelectObject(hdc, CreateSolidBrush(RIGHTBRUSH));
  721. y = rc.top + h * 3 / 4;
  722. PatBlt(hdc, rc.left, y, w, 1, PATCOPY);
  723. for (x=0; x<w; x++) {
  724. i = *((HPINT)lpAudio + muldiv32(x,lLen,w) * 2 + 1);
  725. if (i > 0) {
  726. i = y - (int) ((LONG)i * (h/4) / 32768);
  727. PatBlt(hdc, rc.left+x, i, 1, y-i, PATCOPY);
  728. }
  729. else {
  730. i = (int) ((LONG)i * (h/4) / 32768);
  731. PatBlt(hdc, rc.left+x, y, 1, -i, PATCOPY);
  732. }
  733. }
  734. DeleteObject(SelectObject(hdc, hbr));
  735. }
  736. } // endif stereo
  737. }
  738. /*----------------------------------------------------------------------------*\
  739. | AppWndProc( hwnd, uiMessage, wParam, lParam ) |
  740. | |
  741. | Description: |
  742. | The window proc for the app's main (tiled) window. This processes all |
  743. | of the parent window's messages. |
  744. | |
  745. | Arguments: |
  746. | hwnd window handle for the window |
  747. | uiMessage message number |
  748. | wParam message-dependent |
  749. | lParam message-dependent |
  750. | |
  751. | Returns: |
  752. | 0 if processed, nonzero if ignored |
  753. | |
  754. \*----------------------------------------------------------------------------*/
  755. LONG FAR PASCAL _export AppWndProc(hwnd, msg, wParam, lParam)
  756. HWND hwnd;
  757. unsigned msg;
  758. WPARAM wParam;
  759. LPARAM lParam;
  760. {
  761. PAINTSTRUCT ps;
  762. BOOL f;
  763. HDC hdc;
  764. TCHAR ach[200];
  765. int iFrameWidth;
  766. int iLen;
  767. LONG lFrame, lCurFrame;
  768. int n;
  769. int nFrames;
  770. LPBITMAPINFOHEADER lpbi;
  771. static BYTE abFormat[1024];
  772. LONG l;
  773. LONG lTime;
  774. LONG lSize = 0;
  775. LONG lAudioStart;
  776. LONG lAudioLen;
  777. RECT rcFrame, rcC;
  778. int yStreamTop;
  779. int i;
  780. HBRUSH hbr;
  781. RECT rc;
  782. HRESULT hr;
  783. switch (msg) {
  784. case WM_CREATE:
  785. if (gachFileName[0])
  786. InitAvi(hwnd, gachFileName, MENU_OPEN);
  787. break;
  788. case WM_COMMAND:
  789. return AppCommand(hwnd,msg,wParam,lParam);
  790. case WM_INITMENU:
  791. f = gcpavi > 0;
  792. EnableMenuItem((HMENU)wParam, MENU_SAVE, MF_GRAYED);
  793. EnableMenuItem((HMENU)wParam, MENU_SAVEAS, f ? MF_ENABLED : MF_GRAYED);
  794. EnableMenuItem((HMENU)wParam, MENU_SAVERAW,f ? MF_ENABLED : MF_GRAYED);
  795. EnableMenuItem((HMENU)wParam, MENU_OPTIONS,f ? MF_ENABLED : MF_GRAYED);
  796. f = gcpavi > 0;
  797. EnableMenuItem((HMENU)wParam, MENU_NEW, f ? MF_ENABLED : MF_GRAYED);
  798. EnableMenuItem((HMENU)wParam, MENU_ADD, f ? MF_ENABLED : MF_GRAYED);
  799. EnableMenuItem((HMENU)wParam, MENU_COPY, f ? MF_ENABLED : MF_GRAYED);
  800. EnableMenuItem((HMENU)wParam, MENU_PLAY_STREAM,f ? MF_ENABLED : MF_GRAYED);
  801. f = gpfile != 0;
  802. EnableMenuItem((HMENU)wParam, MENU_CFILE, f ? MF_ENABLED : MF_GRAYED);
  803. EnableMenuItem((HMENU)wParam, MENU_PLAY_FILE, f ? MF_ENABLED : MF_GRAYED);
  804. f = TRUE;
  805. EnableMenuItem((HMENU)wParam, MENU_PASTE, f ? MF_ENABLED : MF_GRAYED);
  806. f = FALSE;
  807. EnableMenuItem((HMENU)wParam, MENU_CUT, f ? MF_ENABLED : MF_GRAYED);
  808. EnableMenuItem((HMENU)wParam, MENU_DELETE, f ? MF_ENABLED : MF_GRAYED);
  809. EnableMenuItem((HMENU)wParam, MENU_MARK, f ? MF_ENABLED : MF_GRAYED);
  810. f = gfAudioFound;
  811. EnableMenuItem((HMENU)wParam, MENU_SAVEWAVE,f ? MF_ENABLED : MF_GRAYED);
  812. f = gfVideoFound || gfAudioFound;
  813. EnableMenuItem((HMENU)wParam, MENU_PLAY,
  814. (f && !gfPlaying) ? MF_ENABLED : MF_GRAYED);
  815. EnableMenuItem((HMENU)wParam, MENU_STOP,
  816. (f && gfPlaying) ? MF_ENABLED : MF_GRAYED);
  817. CheckMenuItem((HMENU)wParam, MENU_ZOOMQUARTER,
  818. (gwZoom == 1) ? MF_CHECKED : MF_UNCHECKED);
  819. CheckMenuItem((HMENU)wParam, MENU_ZOOMHALF,
  820. (gwZoom == 2) ? MF_CHECKED : MF_UNCHECKED);
  821. CheckMenuItem((HMENU)wParam, MENU_ZOOM1,
  822. (gwZoom == 4) ? MF_CHECKED : MF_UNCHECKED);
  823. CheckMenuItem((HMENU)wParam, MENU_ZOOM2,
  824. (gwZoom == 8) ? MF_CHECKED : MF_UNCHECKED);
  825. CheckMenuItem((HMENU)wParam, MENU_ZOOM4,
  826. (gwZoom == 16) ? MF_CHECKED : MF_UNCHECKED);
  827. break;
  828. case WM_NCHITTEST:
  829. if (fWait)
  830. {
  831. lParam = DefWindowProc(hwnd,msg,wParam,lParam);
  832. if (lParam == HTMENU)
  833. lParam = HTCLIENT;
  834. return lParam;
  835. }
  836. break;
  837. case WM_SIZE:
  838. // Set vertical scrollbar for scrolling streams
  839. GetClientRect(hwnd, &rc);
  840. if (vertHeight > rc.bottom) {
  841. vertSBLen = vertHeight - rc.bottom;
  842. SetScrollRange(hwnd, SB_VERT, 0, (int)vertSBLen, TRUE);
  843. } else {
  844. vertSBLen = 0;
  845. SetScrollRange(hwnd, SB_VERT, 0, 0, TRUE);
  846. }
  847. break;
  848. case WM_SETCURSOR:
  849. if (fWait && LOWORD(lParam) == HTCLIENT)
  850. {
  851. SetCursor(LoadCursor(NULL, IDC_WAIT));
  852. return TRUE;
  853. }
  854. break;
  855. case WM_DESTROY:
  856. FreeAvi(hwnd);
  857. RevokeObjects();
  858. AVIStreamExit();
  859. PostQuitMessage(0);
  860. break;
  861. case WM_CLOSE:
  862. if (fWait)
  863. return 0;
  864. break;
  865. case WM_SYSCOMMAND:
  866. switch (wParam & 0xFFF0) {
  867. case SC_KEYMENU:
  868. if (fWait) // block keyboard access to menus if waiting
  869. return 0;
  870. break;
  871. }
  872. break;
  873. case WM_PALETTECHANGED:
  874. if ((HWND)wParam == hwnd)
  875. break;
  876. case WM_QUERYNEWPALETTE:
  877. hdc = GetDC(hwnd);
  878. if (f = DrawDibRealize(ghdd[0], hdc, FALSE)) // !!! stream #
  879. InvalidateRect(hwnd,NULL,TRUE);
  880. ReleaseDC(hwnd,hdc);
  881. return f;
  882. case WM_ERASEBKGND:
  883. break;
  884. case WM_PAINT:
  885. hdc = BeginPaint(hwnd,&ps);
  886. SelectObject(hdc, hfontApp);
  887. #define PRINT(sz) \
  888. (TextOut(hdc, TSPACE, yStreamTop, sz, lstrlen(sz)), \
  889. yStreamTop += tm.tmHeight+1)
  890. #define PF1(sz,a) (wsprintf(ach, sz, a), PRINT(ach))
  891. #define PF2(sz,a,b) (wsprintf(ach, sz, a, b), PRINT(ach))
  892. #define PF3(sz,a,b,c) (wsprintf(ach, sz, a, b, c), PRINT(ach))
  893. #define PF4(sz,a,b,c,d) (wsprintf(ach, sz, a, b, c, d), PRINT(ach))
  894. #define PF5(sz,a,b,c,d,e) (wsprintf(ach, sz, a, b, c, d, e), PRINT(ach))
  895. #define PF6(sz,a,b,c,d,e,f) (wsprintf(ach, sz, a, b, c, d, e, f), PRINT(ach))
  896. #define PF7(sz,a,b,c,d,e,f,g) (wsprintf(ach, sz, a, b, c, d, e, f, g), PRINT(ach))
  897. #define PF8(sz,a,b,c,d,e,f,g,h) (wsprintf(ach, sz, a, b, c, d, e, f, g, h), PRINT(ach))
  898. #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))
  899. GetClientRect(hwnd, &rcC);
  900. lTime = GetScrollTime(hwnd);
  901. yStreamTop = -GetScrollPos(hwnd, SB_VERT);
  902. for (i=0; i<gcpavi; i++) {
  903. AVISTREAMINFO avis;
  904. BOOL fFirstFrame = TRUE;
  905. LONG lPos;
  906. LONG lNearKey;
  907. LONG lPrevKey;
  908. LONG lNextKey;
  909. LONG lNearAny;
  910. LONG lPrevAny;
  911. LONG lNextAny;
  912. LONG lNearFmt;
  913. LONG lPrevFmt;
  914. LONG lNextFmt;
  915. LONG lEnd, lEndTime;
  916. // the idea is to allow stream select!
  917. gaiStreamTop[i] = yStreamTop;
  918. hr = AVIStreamInfo(gapavi[i], &avis, sizeof(avis));
  919. FIXCC(avis.fccHandler);
  920. FIXCC(avis.fccType);
  921. l = sizeof(abFormat);
  922. hr = AVIStreamReadFormat(gapavi[i],0, &abFormat, &l);
  923. #ifdef WIN32
  924. PF7(TEXT("Stream%d [%4.4hs/%4.4hs] Start: %ld Length: %ld (%ld.%03ld sec) "),
  925. i,
  926. (LPSTR)&avis.fccType,
  927. (LPSTR)&avis.fccHandler,
  928. AVIStreamStart(gapavi[i]),
  929. AVIStreamLength(gapavi[i]),
  930. AVIStreamLengthTime(gapavi[i]) / 1000,
  931. AVIStreamLengthTime(gapavi[i]) % 1000);
  932. #else
  933. PF7(TEXT("Stream%d [%4.4ls/%4.4ls] Start: %ld Length: %ld (%ld.%03ld sec) "),
  934. i,
  935. (LPSTR)&avis.fccType,
  936. (LPSTR)&avis.fccHandler,
  937. AVIStreamStart(gapavi[i]),
  938. AVIStreamLength(gapavi[i]),
  939. AVIStreamLengthTime(gapavi[i]) / 1000,
  940. AVIStreamLengthTime(gapavi[i]) % 1000);
  941. #endif
  942. lPos = AVIStreamTimeToSample(gapavi[i], lTime);
  943. AVIStreamSampleSize(gapavi[i], lPos, &lSize);
  944. lNearKey = AVIStreamFindSample(gapavi[i], lPos, FIND_PREV|FIND_KEY);
  945. #ifdef TEST_FINDSAMPLE
  946. lNearAny = AVIStreamFindSample(gapavi[i], lPos, FIND_PREV|FIND_ANY);
  947. lNearFmt = AVIStreamFindSample(gapavi[i], lPos, FIND_PREV|FIND_FORMAT);
  948. lPrevKey = AVIStreamFindSample(gapavi[i], lPos-1, FIND_PREV|FIND_KEY);
  949. lPrevAny = AVIStreamFindSample(gapavi[i], lPos-1, FIND_PREV|FIND_ANY);
  950. lPrevFmt = AVIStreamFindSample(gapavi[i], lPos-1, FIND_PREV|FIND_FORMAT);
  951. lNextKey = AVIStreamFindSample(gapavi[i], lPos+1, FIND_NEXT|FIND_KEY);
  952. lNextAny = AVIStreamFindSample(gapavi[i], lPos+1, FIND_NEXT|FIND_ANY);
  953. lNextFmt = AVIStreamFindSample(gapavi[i], lPos+1, FIND_NEXT|FIND_FORMAT);
  954. #endif
  955. PF5(TEXT("Pos:%ld Time:%ld.%03ld sec Size:%ld bytes %s "),
  956. lPos, lTime/1000, lTime%1000, lSize,
  957. (LPTSTR)(lPos == lNearKey ? TEXT("Key") : TEXT("")));
  958. #ifdef TEST_FINDSAMPLE
  959. PF9(TEXT("PrevKey=%ld NearKey=%ld NextKey=%ld, PrevAny=%ld NearAny=%ld NextAny=%ld, PrevFmt=%ld NearFmt=%ld NextFmt=%ld "),
  960. lPrevKey, lNearKey, lNextKey,
  961. lPrevAny, lNearAny, lNextAny,
  962. lPrevFmt, lNearFmt, lNextFmt);
  963. #endif
  964. if (avis.fccType == streamtypeVIDEO) {
  965. lpbi = (LPBITMAPINFOHEADER)abFormat;
  966. FIXCC(lpbi->biCompression);
  967. //
  968. // display video format
  969. //
  970. // Video: 160x120x8 (cram)
  971. //
  972. #ifdef WIN32
  973. PF4(TEXT("Format: %dx%dx%d (%4.4hs)"),
  974. (int)lpbi->biWidth,
  975. (int)lpbi->biHeight,
  976. (int)lpbi->biBitCount,
  977. (LPSTR)&lpbi->biCompression);
  978. #else
  979. PF4(TEXT("Format: %dx%dx%d (%4.4s)"),
  980. (int)lpbi->biWidth,
  981. (int)lpbi->biHeight,
  982. (int)lpbi->biBitCount,
  983. (LPSTR)&lpbi->biCompression);
  984. #endif
  985. //
  986. // Which frame belongs at this time?
  987. //
  988. lEndTime = AVIStreamEndTime(gapavi[i]);
  989. if (lTime <= lEndTime)
  990. lFrame = AVIStreamTimeToSample(gapavi[i], lTime);
  991. else { // we've scrolled past the end of this stream
  992. lEnd = AVIStreamEnd(gapavi[i]);
  993. lFrame = lEnd + AVIStreamTimeToSample(
  994. gapavi[i], lTime - lEndTime);
  995. }
  996. yStreamTop += TSPACE/2;
  997. // how wide is each frame to paint?
  998. iFrameWidth = (avis.rcFrame.right - avis.rcFrame.left) *
  999. gwZoom / 4 + HSPACE;
  1000. //
  1001. // how many frames can we fit on each half of the screen?
  1002. //
  1003. nFrames = (rcC.right - iFrameWidth) / (2 * iFrameWidth);
  1004. if (nFrames < 0)
  1005. nFrames = 0; // at least draw *something*
  1006. // Step through all the frames we'll draw
  1007. for (n=-nFrames; n<=nFrames; n++)
  1008. {
  1009. // !!! If this code didn't have awful rounding errors,
  1010. // I wouldn't need to special case the first stream.
  1011. if (gapavi[i] == gpaviVideo) {
  1012. // by definition, we know what frame we're drawing.
  1013. lCurFrame = lFrame + n;
  1014. // what time is it at that frame?
  1015. l = AVIStreamSampleToTime(gapavi[i], lCurFrame);
  1016. } else {
  1017. // What time is it at this pixel?
  1018. l = lTime + muldiv32(n * iFrameWidth, gdwMicroSecPerPixel, 1000);
  1019. // Get the frame
  1020. lCurFrame = AVIStreamTimeToSample(gapavi[i], l);
  1021. }
  1022. // !!!
  1023. // Could actually return an LPBI for invalid frames
  1024. // so we better force it to NULL.
  1025. //
  1026. if (gapgf[i] && lCurFrame >= AVIStreamStart(gapavi[i]))
  1027. lpbi = AVIStreamGetFrame(gapgf[i], lCurFrame);
  1028. else
  1029. lpbi = NULL;
  1030. // Location of the current frame
  1031. rcFrame.left = rcC.right / 2 -
  1032. ((avis.rcFrame.right - avis.rcFrame.left) *
  1033. gwZoom/4)/2+ (n * iFrameWidth);
  1034. rcFrame.top = yStreamTop;
  1035. rcFrame.right = rcFrame.left +
  1036. (avis.rcFrame.right - avis.rcFrame.left)*gwZoom/4;
  1037. rcFrame.bottom = rcFrame.top +
  1038. (avis.rcFrame.bottom - avis.rcFrame.top)*gwZoom/4;
  1039. //
  1040. // draw frame around current frame.
  1041. //
  1042. if (n == 0)
  1043. hbr = CreateSolidBrush(RGB(255,0,0));
  1044. else
  1045. hbr = CreateSolidBrush(RGB(255,255,255));
  1046. InflateRect(&rcFrame, 1, 1);
  1047. FrameRect(hdc, &rcFrame, hbr);
  1048. InflateRect(&rcFrame, -1, -1);
  1049. DeleteObject (hbr);
  1050. if (lpbi)
  1051. {
  1052. DrawDibDraw(ghdd[i],hdc,
  1053. rcFrame.left, rcFrame.top,
  1054. rcFrame.right - rcFrame.left,
  1055. rcFrame.bottom - rcFrame.top,
  1056. lpbi, NULL,
  1057. 0, 0, -1, -1,
  1058. ((gapavi[i] == gpaviVideo) && fFirstFrame) ?
  1059. 0 : DDF_BACKGROUNDPAL);
  1060. fFirstFrame = FALSE;
  1061. iLen = wsprintf(ach, TEXT("%ld %ld.%03lds"),
  1062. lCurFrame, l/1000, l%1000);
  1063. }
  1064. else
  1065. {
  1066. if (gapgf[i])
  1067. SelectObject(hdc,GetStockObject(DKGRAY_BRUSH));
  1068. else
  1069. SelectObject(hdc,GetStockObject(LTGRAY_BRUSH));
  1070. PatBlt(hdc,
  1071. rcFrame.left, rcFrame.top,
  1072. rcFrame.right - rcFrame.left,
  1073. rcFrame.bottom - rcFrame.top,
  1074. PATCOPY);
  1075. iLen = 0;
  1076. ach[0] = TEXT('\0');
  1077. }
  1078. rc.left = rcFrame.left;
  1079. rc.right = rcFrame.right + HSPACE;
  1080. rc.top = rcFrame.bottom + 2;
  1081. rc.bottom = rc.top + TSPACE;
  1082. ExtTextOut(hdc, rc.left, rc.top, ETO_OPAQUE,
  1083. &rc, ach, iLen, NULL);
  1084. }
  1085. yStreamTop += TSPACE + avis.rcFrame.bottom*gwZoom/4;
  1086. }
  1087. else if (avis.fccType == streamtypeAUDIO)
  1088. {
  1089. LPWAVEFORMAT pwf = (LPWAVEFORMAT)abFormat;
  1090. TCHAR *szFmt;
  1091. if (pwf->wFormatTag == 1) { // PCM
  1092. if (pwf->nChannels == 1)
  1093. szFmt = TEXT("Format: Mono %dHz %dbit");
  1094. else
  1095. szFmt = TEXT("Format: Stereo %dHz %dbit");
  1096. }
  1097. else if (pwf->wFormatTag == 2) { // ADPCM
  1098. if (pwf->nChannels == 1)
  1099. szFmt = TEXT("Format: ADPCM Mono %dHz %dbit");
  1100. else
  1101. szFmt = TEXT("Format: ADPCM Stereo %dHz %dbit");
  1102. }
  1103. else {
  1104. if (pwf->nChannels == 1)
  1105. szFmt = TEXT("Format: Compressed Mono %dHz %dbit");
  1106. else
  1107. szFmt = TEXT("Format: Compressed Stereo %dHz %dbit");
  1108. }
  1109. PF2(szFmt,(int)pwf->nSamplesPerSec,
  1110. (int)(pwf->nAvgBytesPerSec * 8 / pwf->nSamplesPerSec));
  1111. lAudioStart = lTime - muldiv32(rcC.right / 2,
  1112. gdwMicroSecPerPixel, 1000);
  1113. lAudioLen = 2 * (lTime - lAudioStart);
  1114. // !!! Fix the SAMPLE field for short audio clips !!!
  1115. //PF2("Sample:%ld %ld ",
  1116. // AVIStreamTimeToSample(gapavi[i], lAudioStart),
  1117. // AVIStreamTimeToSample(gapavi[i], lAudioLen));
  1118. /* Make rectangle to draw audio into */
  1119. rc.left = rcC.left;
  1120. rc.right = rcC.right;
  1121. rc.top = yStreamTop;
  1122. rc.bottom = rc.top + AUDIOVSPACE * gwZoom / 4;
  1123. PaintAudio(hdc, &rc, gapavi[i], lAudioStart, lAudioLen);
  1124. yStreamTop += rc.bottom - rc.top;
  1125. }
  1126. else if (avis.fccType == streamtypeTEXT)
  1127. {
  1128. LONG lPos;
  1129. int iLeft;
  1130. lPos = AVIStreamTimeToSample(gapavi[i],
  1131. lTime -
  1132. muldiv32((rcC.right - rcC.left),
  1133. gdwMicroSecPerPixel,
  1134. 1000));
  1135. if (lPos < 0)
  1136. lPos = 0;
  1137. PatBlt(hdc, rcC.left, yStreamTop,
  1138. rcC.right - rcC.left, TSPACE + TSPACE,
  1139. WHITENESS);
  1140. while (lPos < AVIStreamEnd(gapavi[i]) - 1) {
  1141. // What pixel is it at this time?
  1142. iLeft = (rcC.right + rcC.left) / 2 +
  1143. (int) muldiv32(AVIStreamSampleToTime(gapavi[i], lPos) - lTime,
  1144. 1000, gdwMicroSecPerPixel);
  1145. if (iLeft >= rcC.right)
  1146. break;
  1147. AVIStreamRead(gapavi[i], lPos, 1, ach, sizeof(ach), &l, NULL);
  1148. if (l)
  1149. TextOut(hdc, iLeft, yStreamTop, ach, (int) l - 1);
  1150. iLen = wsprintf(ach, TEXT("%ld"), lPos);
  1151. TextOut(hdc, iLeft, yStreamTop + TSPACE, ach, iLen);
  1152. #if 0
  1153. if (ghic[i]) {
  1154. ICDrawBegin(ghic[i],
  1155. 0,
  1156. 0,
  1157. hwnd,
  1158. hdc,
  1159. iLeft,
  1160. yStreamTop,
  1161. 100,
  1162. 50,
  1163. NULL,
  1164. 0,
  1165. 0,
  1166. -1,
  1167. -1,
  1168. 1,
  1169. 1);
  1170. ICDraw(ghic[i], 0,
  1171. NULL, ach, l, lPos);
  1172. ICDrawEnd(ghic[i]);
  1173. }
  1174. #endif
  1175. lPos += 1;
  1176. }
  1177. yStreamTop += TSPACE + TSPACE;
  1178. }
  1179. else
  1180. {
  1181. }
  1182. yStreamTop += VSPACE;
  1183. if (yStreamTop >= rcC.bottom)
  1184. break;
  1185. }
  1186. EndPaint(hwnd,&ps);
  1187. break;
  1188. case WM_KEYDOWN:
  1189. switch (wParam)
  1190. {
  1191. case VK_UP: PostMessage (hwnd,WM_VSCROLL,SB_LINEUP,0L); break;
  1192. case VK_DOWN: PostMessage (hwnd,WM_VSCROLL,SB_LINEDOWN,0L); break;
  1193. case VK_PRIOR: PostMessage (hwnd,WM_HSCROLL,SB_PAGEUP,0L); break;
  1194. case VK_NEXT: PostMessage (hwnd,WM_HSCROLL,SB_PAGEDOWN,0L); break;
  1195. case VK_HOME: PostMessage (hwnd,WM_HSCROLL,SB_THUMBPOSITION,0L); break;
  1196. case VK_END: PostMessage (hwnd,WM_HSCROLL,SB_THUMBPOSITION,0x7FFF); break;
  1197. case VK_LEFT: PostMessage (hwnd,WM_HSCROLL,SB_LINEUP,0L); break;
  1198. case VK_RIGHT: PostMessage (hwnd,WM_HSCROLL,SB_LINEDOWN,0L); break;
  1199. }
  1200. break;
  1201. case WM_HSCROLL:
  1202. l = GetScrollTime(hwnd);
  1203. switch (GET_WM_HSCROLL_CODE(wParam, lParam)) {
  1204. case SB_LINEDOWN: l += timehscroll; break;
  1205. case SB_LINEUP: l -= timehscroll; break;
  1206. case SB_PAGEDOWN: l += timeLength/10; break;
  1207. case SB_PAGEUP: l -= timeLength/10; break;
  1208. case SB_THUMBTRACK:
  1209. case SB_THUMBPOSITION:
  1210. l = GET_WM_HSCROLL_POS(wParam, lParam);
  1211. l = timeStart + muldiv32(l, timeLength, SCROLLRANGE);
  1212. break;
  1213. }
  1214. if (l < timeStart)
  1215. l = timeStart;
  1216. if (l > timeEnd)
  1217. l = timeEnd;
  1218. if (l == GetScrollTime(hwnd))
  1219. break;
  1220. SetScrollTime(hwnd, l);
  1221. InvalidateRect(hwnd, NULL, FALSE);
  1222. UpdateWindow(hwnd);
  1223. break;
  1224. case WM_VSCROLL:
  1225. l = GetScrollPos(hwnd, SB_VERT);
  1226. GetClientRect(hwnd, &rc);
  1227. switch (GET_WM_VSCROLL_CODE(wParam, lParam)) {
  1228. case SB_LINEDOWN: l += 10; break;
  1229. case SB_LINEUP: l -= 10; break;
  1230. case SB_PAGEDOWN: l += rc.bottom; break;
  1231. case SB_PAGEUP: l -= rc.bottom; break;
  1232. case SB_THUMBTRACK:
  1233. case SB_THUMBPOSITION:
  1234. l = GET_WM_VSCROLL_POS(wParam, lParam);
  1235. break;
  1236. }
  1237. if (l < 0)
  1238. l = 0;
  1239. if (l > vertSBLen)
  1240. l = vertSBLen;
  1241. if (l == GetScrollPos(hwnd, SB_VERT))
  1242. break;
  1243. SetScrollPos(hwnd, SB_VERT, (int)l, TRUE);
  1244. InvalidateRect(hwnd, NULL, TRUE);
  1245. UpdateWindow(hwnd);
  1246. break;
  1247. case MM_WOM_OPEN:
  1248. case MM_WOM_DONE:
  1249. case MM_WOM_CLOSE:
  1250. aviaudioMessage(hwnd, msg, wParam, lParam);
  1251. break;
  1252. }
  1253. return DefWindowProc(hwnd,msg,wParam,lParam);
  1254. }
  1255. BOOL FAR PASCAL _export SaveCallback(int iProgress)
  1256. {
  1257. TCHAR ach[128];
  1258. wsprintf(ach, TEXT("%s - Saving %s: %d%%"),
  1259. (LPTSTR) gszAppName, (LPTSTR) gachSaveFileName, iProgress);
  1260. SetWindowText(ghwndApp, ach);
  1261. return WinYield();
  1262. }
  1263. LONG NEAR PASCAL AppCommand (hwnd, msg, wParam, lParam)
  1264. HWND hwnd;
  1265. unsigned msg;
  1266. WPARAM wParam;
  1267. LPARAM lParam;
  1268. {
  1269. OPENFILENAME ofn;
  1270. TCHAR achFilter[128];
  1271. switch(GET_WM_COMMAND_ID(wParam, lParam))
  1272. {
  1273. case MENU_EXIT:
  1274. PostMessage(hwnd,WM_CLOSE,0,0L);
  1275. break;
  1276. // Set the compression options for each stream - pass an array of
  1277. // streams and an array of compression options structures
  1278. case MENU_OPTIONS:
  1279. AVISaveOptions(hwnd, ICMF_CHOOSE_KEYFRAME | ICMF_CHOOSE_DATARATE
  1280. | ICMF_CHOOSE_PREVIEW,
  1281. gcpavi, gapavi, galpAVIOptions);
  1282. break;
  1283. case MENU_SAVEAS:
  1284. case MENU_SAVERAW:
  1285. case MENU_SAVESMALL:
  1286. gachSaveFileName[0] = 0;
  1287. /* prompt user for file to open */
  1288. ofn.lStructSize = sizeof(OPENFILENAME);
  1289. ofn.hwndOwner = hwnd;
  1290. ofn.hInstance = NULL;
  1291. AVIBuildFilter(achFilter, sizeof(achFilter)/sizeof(TCHAR), TRUE);
  1292. ofn.lpstrFilter = achFilter;
  1293. ofn.lpstrCustomFilter = NULL;
  1294. ofn.nMaxCustFilter = 0;
  1295. ofn.nFilterIndex = 0;
  1296. ofn.lpstrFile = gachSaveFileName;
  1297. ofn.nMaxFile = sizeof(gachSaveFileName)/sizeof(TCHAR);
  1298. ofn.lpstrFileTitle = NULL;
  1299. ofn.nMaxFileTitle = 0;
  1300. ofn.lpstrInitialDir = NULL;
  1301. if (GET_WM_COMMAND_ID(wParam, lParam) == MENU_SAVEAS)
  1302. ofn.lpstrTitle = TEXT("Save AVI File");
  1303. else if (GET_WM_COMMAND_ID(wParam, lParam) == MENU_SAVERAW)
  1304. ofn.lpstrTitle = TEXT("Save Raw");
  1305. else if (GET_WM_COMMAND_ID(wParam, lParam) == MENU_SAVESMALL)
  1306. ofn.lpstrTitle = TEXT("Save Small");
  1307. else
  1308. ofn.lpstrTitle = TEXT("Please give me a title");
  1309. ofn.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY |
  1310. OFN_OVERWRITEPROMPT;
  1311. ofn.nFileOffset = 0;
  1312. ofn.nFileExtension = 0;
  1313. ofn.lpstrDefExt = TEXT("avi");
  1314. ofn.lCustData = 0;
  1315. ofn.lpfnHook = NULL;
  1316. ofn.lpTemplateName = NULL;
  1317. if (GetSaveFileName(&ofn))
  1318. {
  1319. FARPROC lpfn = MakeProcInstance(SaveCallback, ghInstApp);
  1320. if (lpfn)
  1321. {
  1322. DWORD fccHandler[MAXNUMSTREAMS];
  1323. int i;
  1324. StartWait();
  1325. for (i = 0; i < gcpavi; i++)
  1326. fccHandler[i] = galpAVIOptions[i]->fccHandler;
  1327. // We only want to save the first video stream
  1328. if (GET_WM_COMMAND_ID(wParam, lParam) == MENU_SAVESMALL) {
  1329. SaveSmall(gpaviVideo, gachSaveFileName);
  1330. EndWait();
  1331. break;
  1332. }
  1333. // !!! This won't take away audio compression !!!
  1334. // We want to save raw -- don't use any video compression
  1335. if (GET_WM_COMMAND_ID(wParam, lParam) == MENU_SAVERAW)
  1336. for (i = 0; i < gcpavi; i++)
  1337. galpAVIOptions[i]->fccHandler = 0;
  1338. AVISaveV(gachSaveFileName,
  1339. NULL,
  1340. (AVISAVECALLBACK) lpfn,
  1341. gcpavi,
  1342. gapavi,
  1343. galpAVIOptions);
  1344. // !!! error check?
  1345. // Now put the video compressors back that we stole
  1346. for (i = 0; i < gcpavi; i++)
  1347. galpAVIOptions[i]->fccHandler = fccHandler[i];
  1348. EndWait();
  1349. FreeProcInstance(lpfn);
  1350. FixWindowTitle(hwnd);
  1351. }
  1352. }
  1353. break;
  1354. case MENU_NEW:
  1355. FreeAvi(hwnd);
  1356. gachFileName[0] = TEXT('\0');
  1357. FixWindowTitle(hwnd);
  1358. break;
  1359. case MENU_OPEN:
  1360. case MENU_ADD:
  1361. gachFileName[0] = 0;
  1362. /* prompt user for file to open */
  1363. ofn.lStructSize = sizeof(OPENFILENAME);
  1364. ofn.hwndOwner = hwnd;
  1365. ofn.hInstance = NULL;
  1366. if (wParam == MENU_ADD)
  1367. {
  1368. ofn.lpstrTitle = TEXT("Merge With");
  1369. }
  1370. else
  1371. {
  1372. ofn.lpstrTitle = TEXT("Open AVI");
  1373. }
  1374. if (gachFilter[0] == TEXT('\0'))
  1375. AVIBuildFilter(gachFilter, sizeof(gachFilter)/sizeof(TCHAR), FALSE);
  1376. ofn.lpstrFilter = gachFilter;
  1377. ofn.lpstrCustomFilter = NULL;
  1378. ofn.nMaxCustFilter = 0;
  1379. ofn.nFilterIndex = 0;
  1380. ofn.lpstrFile = gachFileName;
  1381. ofn.nMaxFile = sizeof(gachFileName)/sizeof(TCHAR);
  1382. ofn.lpstrFileTitle = NULL;
  1383. ofn.nMaxFileTitle = 0;
  1384. ofn.lpstrInitialDir = NULL;
  1385. ofn.Flags = OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY;
  1386. ofn.nFileOffset = 0;
  1387. ofn.nFileExtension = 0;
  1388. ofn.lpstrDefExt = NULL;
  1389. ofn.lCustData = 0;
  1390. ofn.lpfnHook = NULL;
  1391. ofn.lpTemplateName = NULL;
  1392. if (GetOpenFileNamePreview(&ofn))
  1393. {
  1394. InitAvi(hwnd, gachFileName, wParam);
  1395. }
  1396. break;
  1397. case MENU_BALL:
  1398. InitBall(hwnd);
  1399. break;
  1400. case MENU_ZOOMQUARTER:
  1401. gwZoom = 1;
  1402. FixScrollbars(hwnd);
  1403. InvalidateRect(hwnd, NULL, TRUE);
  1404. break;
  1405. case MENU_ZOOMHALF:
  1406. gwZoom = 2;
  1407. FixScrollbars(hwnd);
  1408. InvalidateRect(hwnd, NULL, TRUE);
  1409. break;
  1410. case MENU_ZOOM1:
  1411. gwZoom = 4;
  1412. FixScrollbars(hwnd);
  1413. InvalidateRect(hwnd, NULL, TRUE);
  1414. break;
  1415. case MENU_ZOOM2:
  1416. gwZoom = 8;
  1417. FixScrollbars(hwnd);
  1418. InvalidateRect(hwnd, NULL, TRUE);
  1419. break;
  1420. case MENU_ZOOM4:
  1421. gwZoom = 16;
  1422. FixScrollbars(hwnd);
  1423. InvalidateRect(hwnd, NULL, TRUE);
  1424. break;
  1425. //
  1426. // readly play the file, via MCI
  1427. //
  1428. case MENU_PLAY_STREAM:
  1429. case MENU_PLAY_FILE:
  1430. {
  1431. TCHAR ach[80];
  1432. HWND hwndMci = MCIWndCreate(NULL, ghInstApp,
  1433. MCIWNDF_SHOWNAME | MCIWNDF_SHOWMODE |
  1434. WS_OVERLAPPEDWINDOW | WS_VISIBLE,
  1435. NULL);
  1436. if (hwndMci) {
  1437. if (GET_WM_COMMAND_ID(wParam,lParam) == MENU_PLAY_FILE)
  1438. wsprintf(ach, TEXT("AVIVideo!@%ld"), gpfile);
  1439. else
  1440. wsprintf(ach, TEXT("AVIVideo!@%ld"), gapavi[giCurrentStream]);
  1441. MCIWndOpen(hwndMci, ach, 0);
  1442. MCIWndPlay(hwndMci);
  1443. }
  1444. }
  1445. break;
  1446. //
  1447. // Simulate playing the file. We just play the 1st audio stream and let
  1448. // our main message loop scroll the video by whenever it's bored.
  1449. //
  1450. case MENU_PLAY:
  1451. if (gfAudioFound)
  1452. aviaudioPlay(hwnd,
  1453. gpaviAudio,
  1454. AVIStreamTimeToSample(gpaviAudio, GetScrollTime(hwnd)),
  1455. AVIStreamEnd(gpaviAudio),
  1456. FALSE);
  1457. gfPlaying = TRUE;
  1458. glPlayStartTime = timeGetTime();
  1459. glPlayStartPos = GetScrollTime(hwnd);
  1460. break;
  1461. //
  1462. // Stop the play preview
  1463. //
  1464. case MENU_STOP:
  1465. if (gfAudioFound)
  1466. aviaudioStop();
  1467. gfPlaying = FALSE;
  1468. break;
  1469. #ifdef CLIPSTUFF
  1470. case MENU_COPY:
  1471. {
  1472. PAVIFILE pf;
  1473. AVIMakeFileFromStreams(&pf, gcpavi, gapavi);
  1474. AVIPutFileOnClipboard(pf);
  1475. AVIFileClose(pf);
  1476. }
  1477. break;
  1478. case MENU_CFILE:
  1479. AVIPutFileOnClipboard(gpfile);
  1480. break;
  1481. case MENU_PASTE:
  1482. {
  1483. PAVIFILE pf = NULL;
  1484. AVIGetFromClipboard(&pf);
  1485. if (pf) {
  1486. DPF("Pasting file from clipboard....\n");
  1487. FreeDrawStuff(hwnd);
  1488. InsertAVIFile(pf, hwnd, TEXT("Clipboard"));
  1489. } else {
  1490. DPF("Can't get file from clipboard....\n");
  1491. }
  1492. break;
  1493. }
  1494. #endif
  1495. }
  1496. return 0L;
  1497. }
  1498. /*----------------------------------------------------------------------------*\
  1499. | ErrMsg - Opens a Message box with a error message in it. The user can |
  1500. | select the OK button to continue |
  1501. \*----------------------------------------------------------------------------*/
  1502. int ErrMsg (LPTSTR sz,...)
  1503. {
  1504. static TCHAR ach[2000];
  1505. va_list va;
  1506. va_start(va, sz);
  1507. wvsprintf (ach,sz, va);
  1508. va_end(va);
  1509. MessageBox(NULL,ach,NULL,
  1510. #ifdef BIDI
  1511. MB_RTL_READING |
  1512. #endif
  1513. MB_OK|MB_ICONEXCLAMATION|MB_TASKMODAL);
  1514. return FALSE;
  1515. }
  1516. /*----------------------------------------------------------------------------*\
  1517. | fDialog(id,hwnd,fpfn) |
  1518. | |
  1519. | Description: |
  1520. | This function displays a dialog box and returns the exit code. |
  1521. | the function passed will have a proc instance made for it. |
  1522. | |
  1523. | Arguments: |
  1524. | id resource id of dialog to display |
  1525. | hwnd parent window of dialog |
  1526. | fpfn dialog message function |
  1527. | |
  1528. | Returns: |
  1529. | exit code of dialog (what was passed to EndDialog) |
  1530. | |
  1531. \*----------------------------------------------------------------------------*/
  1532. BOOL fDialog(int id,HWND hwnd,FARPROC fpfn)
  1533. {
  1534. BOOL f;
  1535. HANDLE hInst;
  1536. hInst = GetWindowInstance(hwnd);
  1537. fpfn = MakeProcInstance(fpfn,hInst);
  1538. f = DialogBox(hInst,MAKEINTRESOURCE(id),hwnd,fpfn);
  1539. FreeProcInstance (fpfn);
  1540. return f;
  1541. }
  1542. /*****************************************************************************
  1543. *
  1544. * dprintf() is called by the DPF macro if DEBUG is defined at compile time.
  1545. *
  1546. * The messages will be send to COM1: like any debug message. To
  1547. * enable debug output, add the following to WIN.INI :
  1548. *
  1549. * [debug]
  1550. * ICSAMPLE=1
  1551. *
  1552. ****************************************************************************/
  1553. #ifdef DEBUG
  1554. #define MODNAME "AVIVIEW"
  1555. static void FAR cdecl dprintf(LPSTR szFormat, ...)
  1556. {
  1557. char ach[128];
  1558. static BOOL fDebug = -1;
  1559. va_list va;
  1560. if (fDebug == -1)
  1561. fDebug = GetProfileIntA("Debug", MODNAME, FALSE);
  1562. if (!fDebug)
  1563. return;
  1564. lstrcpyA(ach, MODNAME ": ");
  1565. va_start(va, szFormat);
  1566. wvsprintfA(ach+lstrlenA(ach),szFormat, va);
  1567. va_end(va);
  1568. // lstrcat(ach, "\r\n");
  1569. OutputDebugStringA(ach);
  1570. }
  1571. #endif
  1572. // !!! function that makes DIBs half as big
  1573. BOOL CrunchDIB(
  1574. LPBITMAPINFOHEADER lpbiSrc, // BITMAPINFO of source
  1575. LPVOID lpSrc, // input bits to crunch
  1576. LPBITMAPINFOHEADER lpbiDst, // BITMAPINFO of dest
  1577. LPVOID lpDst); // output bits to crunch
  1578. //
  1579. // Save a video stream into a new file, after calling CrunchDib on each frame...
  1580. //
  1581. void SaveSmall(PAVISTREAM ps, LPTSTR lpFilename)
  1582. {
  1583. PAVIFILE pf;
  1584. PAVISTREAM psSmall = NULL;
  1585. HRESULT hr;
  1586. AVISTREAMINFO strhdr;
  1587. BITMAPINFOHEADER bi;
  1588. BITMAPINFOHEADER biNew;
  1589. LONG l;
  1590. LPVOID lpOld = NULL;
  1591. LPVOID lpNew = NULL;
  1592. AVIStreamFormatSize(ps, 0, &l);
  1593. if (l > sizeof(bi))
  1594. return;
  1595. l = sizeof(bi);
  1596. hr = AVIStreamReadFormat(ps, 0, &bi, &l);
  1597. if (bi.biCompression != BI_RGB)
  1598. return;
  1599. hr = AVIStreamInfo(ps, &strhdr, sizeof(strhdr));
  1600. hr = AVIFileOpen(&pf, lpFilename, OF_WRITE | OF_CREATE, NULL);
  1601. if (hr != 0)
  1602. return;
  1603. biNew = bi;
  1604. biNew.biWidth /= 2;
  1605. biNew.biHeight /= 2;
  1606. biNew.biSizeImage = ((((UINT)biNew.biBitCount * biNew.biWidth + 31)&~31) / 8) *
  1607. biNew.biHeight;
  1608. SetRect(&strhdr.rcFrame, 0, 0, (int) biNew.biWidth, (int) biNew.biHeight);
  1609. hr = AVIFileCreateStream(pf, &psSmall, &strhdr);
  1610. if (hr != 0) {
  1611. goto exit;
  1612. }
  1613. hr = AVIStreamSetFormat(psSmall, 0, &biNew, sizeof(biNew));
  1614. if (hr != 0) {
  1615. goto exit;
  1616. }
  1617. lpOld = GlobalAllocPtr(GMEM_MOVEABLE, bi.biSizeImage);
  1618. lpNew = GlobalAllocPtr(GMEM_MOVEABLE, biNew.biSizeImage);
  1619. if (!lpOld || !lpNew) {
  1620. goto exit;
  1621. }
  1622. for (l = AVIStreamStart(ps); l < AVIStreamEnd(ps); l++) {
  1623. hr = AVIStreamRead(ps, l, 1, lpOld, bi.biSizeImage, NULL, NULL);
  1624. // !!! error check
  1625. CrunchDIB(&bi, lpOld, &biNew, lpNew);
  1626. hr = AVIStreamWrite(psSmall, l, 1, lpNew, biNew.biSizeImage, AVIIF_KEYFRAME, NULL, NULL);
  1627. // !!! error check
  1628. }
  1629. exit:
  1630. if (lpOld)
  1631. GlobalFreePtr(lpOld);
  1632. if (lpNew)
  1633. GlobalFreePtr(lpNew);
  1634. if (psSmall)
  1635. AVIStreamClose(psSmall);
  1636. AVIFileClose(pf);
  1637. }