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.

996 lines
34 KiB

  1. /****************************************************************************
  2. *
  3. * capmci.c
  4. *
  5. * Control of MCI devices during capture.
  6. *
  7. * Microsoft Video for Windows Sample Capture Class
  8. *
  9. * Copyright (c) 1992, 1993 Microsoft Corporation. All Rights Reserved.
  10. *
  11. * You have a royalty-free right to use, modify, reproduce and
  12. * distribute the Sample Files (and/or any modified version) in
  13. * any way you find useful, provided that you agree that
  14. * Microsoft has no warranty obligations or liability for any
  15. * Sample Application Files which are modified.
  16. *
  17. ***************************************************************************/
  18. #include <windows.h>
  19. #include <windowsx.h>
  20. #include <mmsystem.h>
  21. #include <memory.h> // for _fmemset
  22. #include <stdlib.h>
  23. #include <ctype.h>
  24. #include <string.h>
  25. #include <msvideo.h>
  26. #include <drawdib.h>
  27. #include <mmddk.h>
  28. #include "avicap.h"
  29. #include "avicapi.h"
  30. #ifdef _DEBUG
  31. #define DSTATUS(lpcs, sz) statusUpdateStatus(lpcs, IDS_CAP_INFO, (LPSTR) sz)
  32. #else
  33. #define DSTATUS(lpcs, sz)
  34. #endif
  35. DWORD SendDriverFormat (LPCAPSTREAM lpcs, LPBITMAPINFOHEADER lpbih, DWORD dwInfoHeaderSize);
  36. /*--------------------------------------------------------------+
  37. | TimeMSToHMSString() - change milliseconds into SMPTE time |
  38. +--------------------------------------------------------------*/
  39. void FAR PASCAL TimeMSToSMPTE (DWORD dwMS, LPSTR lpTime)
  40. {
  41. DWORD dwTotalSecs;
  42. LONG lHundredths;
  43. WORD wSecs;
  44. WORD wMins;
  45. WORD wHours;
  46. /* convert to number of seconds */
  47. dwTotalSecs = dwMS / 1000;
  48. /* keep the remainder part */
  49. lHundredths = (dwMS - (dwTotalSecs * 1000)) / 10;
  50. /* break down into other components */
  51. wHours = (WORD)(dwTotalSecs / 3600); // get # Hours
  52. dwTotalSecs -= (wHours * 3600);
  53. wMins = (WORD)(dwTotalSecs / 60); // get # Mins
  54. dwTotalSecs -= (wMins * 60);
  55. wSecs = (WORD)dwTotalSecs; // what's left is # seconds
  56. /* build the string */
  57. /* KLUDGE, force hundredths to SMPTE approximation of PAL frames */
  58. wsprintf((char far *)lpTime, "%02u:%02u:%02u:%02lu", wHours, wMins,
  59. wSecs, (lHundredths * 25) / 100);
  60. }
  61. /*--------------------------------------------------------------+
  62. | START OF MCI CONTROL SECTION |
  63. +--------------------------------------------------------------*/
  64. /*
  65. * CountMCIDevicesByType
  66. * Returns a count of the number of VCR or Videodisc
  67. * devices that MCI claims to know about.
  68. */
  69. int CountMCIDevicesByType ( WORD wType )
  70. {
  71. int nTotal;
  72. DWORD dwCount;
  73. MCI_SYSINFO_PARMS mciSIP;
  74. mciSIP.dwCallback = NULL;
  75. mciSIP.lpstrReturn = (LPSTR) (LPVOID) &dwCount;
  76. mciSIP.dwRetSize = sizeof (dwCount);
  77. mciSIP.wDeviceType = wType;
  78. if (!mciSendCommand (NULL, MCI_SYSINFO, MCI_SYSINFO_QUANTITY,
  79. (DWORD) (LPVOID) &mciSIP))
  80. nTotal = (int) *( (LPDWORD) mciSIP.lpstrReturn);
  81. return nTotal;
  82. }
  83. /*
  84. * MCIDeviceClose
  85. * This routine closes the open MCI device.
  86. */
  87. void MCIDeviceClose (LPCAPSTREAM lpcs)
  88. {
  89. mciSendString( "close mciframes", NULL, 0, NULL );
  90. }
  91. /*
  92. * MCIDeviceOpen
  93. * This routine opens the mci device for use, and sets the
  94. * time format to milliseconds.
  95. * Return FALSE on error;
  96. */
  97. BOOL MCIDeviceOpen (LPCAPSTREAM lpcs)
  98. {
  99. char ach[160];
  100. wsprintf( ach, "open %s shareable alias mciframes",
  101. (LPSTR) lpcs-> achMCIDevice);
  102. lpcs-> dwMCIError = mciSendString( ach, NULL, 0, NULL );
  103. if( lpcs-> dwMCIError ) {
  104. DPF (" MCI Error, open %s shareable alias mciframes", lpcs-> achMCIDevice);
  105. goto err_return;
  106. }
  107. lpcs-> dwMCIError = mciSendString( "set mciframes time format milliseconds",
  108. NULL, 0, NULL );
  109. if( lpcs-> dwMCIError ) {
  110. DPF (" MCI Error, set mciframes time format milliseconds");
  111. goto err_close;
  112. }
  113. return ( TRUE );
  114. err_close:
  115. MCIDeviceClose (lpcs);
  116. err_return:
  117. return ( FALSE );
  118. }
  119. /*
  120. * MCIDeviceGetPosition
  121. * Stores the current device position in milliseconds in lpdwPos.
  122. * Returns TRUE on success, FALSE if error.
  123. */
  124. BOOL FAR PASCAL MCIDeviceGetPosition (LPCAPSTREAM lpcs, LPDWORD lpdwPos)
  125. {
  126. char ach[80];
  127. LPSTR p;
  128. LONG lv;
  129. lpcs-> dwMCIError = mciSendString( "status mciframes position wait",
  130. ach, sizeof(ach), NULL );
  131. if( lpcs-> dwMCIError ) {
  132. DPF (" MCI Error, status mciframes position wait");
  133. *lpdwPos = 0L;
  134. return FALSE;
  135. }
  136. p = ach;
  137. while (*p == ' ') p++;
  138. for (lv = 0; *p >= '0' && *p <= '9'; p++)
  139. lv = (10 * lv) + (*p - '0');
  140. *lpdwPos = lv;
  141. return TRUE;
  142. }
  143. /*
  144. * MCIDeviceSetPosition
  145. * Sets the current device position in milliseconds.
  146. * Returns TRUE on success, FALSE if error.
  147. */
  148. BOOL FAR PASCAL MCIDeviceSetPosition (LPCAPSTREAM lpcs, DWORD dwPos)
  149. {
  150. char achCommand[40];
  151. char ach[80];
  152. lpcs-> dwMCIError = mciSendString( "pause mciframes wait", ach, sizeof(ach), NULL );
  153. if (lpcs-> dwMCIError) {
  154. DPF (" MCI Error, pause mciframes wait");
  155. return FALSE;
  156. }
  157. wsprintf(achCommand, "seek mciframes to %ld wait", dwPos);
  158. lpcs-> dwMCIError = mciSendString( achCommand, ach, sizeof(ach), NULL );
  159. if (lpcs-> dwMCIError)
  160. DPF (" MCI Error, seek mciframes to %ld wait", dwPos);
  161. return ( lpcs-> dwMCIError == 0 ? TRUE : FALSE );
  162. }
  163. /*
  164. * MCIDevicePlay
  165. * Start playing the current MCI device from the current position
  166. * Returns TRUE on success, FALSE if error.
  167. */
  168. BOOL FAR PASCAL MCIDevicePlay (LPCAPSTREAM lpcs)
  169. {
  170. char ach[80];
  171. lpcs-> dwMCIError = mciSendString( "play mciframes", ach, sizeof(ach), NULL );
  172. if (lpcs-> dwMCIError)
  173. DPF (" MCI Error, play mciframes");
  174. return ( lpcs-> dwMCIError == 0 ? TRUE : FALSE );
  175. }
  176. /*
  177. * MCIDevicePause
  178. * Pauses the current MCI device at the current position
  179. * Returns TRUE on success, FALSE if error.
  180. */
  181. BOOL FAR PASCAL MCIDevicePause (LPCAPSTREAM lpcs)
  182. {
  183. char ach[80];
  184. lpcs-> dwMCIError = mciSendString( "pause mciframes wait", ach, sizeof(ach), NULL );
  185. if (lpcs-> dwMCIError)
  186. DPF (" MCI Error, pause mciframes wait");
  187. return ( lpcs-> dwMCIError == 0 ? TRUE : FALSE );
  188. }
  189. /*
  190. * MCIDeviceStop
  191. * Stops the current MCI device
  192. * Returns TRUE on success, FALSE if error.
  193. */
  194. BOOL FAR PASCAL MCIDeviceStop (LPCAPSTREAM lpcs)
  195. {
  196. char ach[80];
  197. lpcs-> dwMCIError = mciSendString( "stop mciframes wait", ach, sizeof(ach), NULL );
  198. if (lpcs-> dwMCIError)
  199. DPF (" MCI Error, stop mciframes wait");
  200. return ( lpcs-> dwMCIError == 0 ? TRUE : FALSE );
  201. }
  202. /*
  203. * MCIDeviceStep
  204. * Step the current MCI at the current position
  205. * Returns TRUE on success, FALSE if error.
  206. */
  207. BOOL FAR PASCAL MCIDeviceStep (LPCAPSTREAM lpcs, BOOL fForward)
  208. {
  209. char ach[80];
  210. lpcs-> dwMCIError = mciSendString( fForward ? "step mciframes wait" :
  211. "step mciframes reverse wait", ach, sizeof(ach), NULL );
  212. if (lpcs-> dwMCIError)
  213. DPF (" MCI Error, step mciframes wait");
  214. return ( lpcs-> dwMCIError == 0 ? TRUE : FALSE );
  215. }
  216. /*
  217. * MCIDeviceFreeze
  218. * freeze the current frame
  219. * Returns TRUE on success, FALSE if error.
  220. */
  221. BOOL FAR PASCAL MCIDeviceFreeze(LPCAPSTREAM lpcs, BOOL fFreeze)
  222. {
  223. lpcs-> dwMCIError = mciSendString( fFreeze ? "freeze mciframes wait" :
  224. "unfreeze mciframes wait", NULL, 0, NULL);
  225. if (lpcs-> dwMCIError)
  226. DPF (" MCI Error, freeze mciframes wait");
  227. return ( lpcs-> dwMCIError == 0 ? TRUE : FALSE );
  228. }
  229. /*
  230. * MCIStepCapture
  231. * Main routine for performing MCI step capture.
  232. *
  233. */
  234. void FAR PASCAL _loadds MCIStepCapture (LPCAPSTREAM lpcs)
  235. {
  236. BOOL fOK = TRUE;
  237. BOOL fT;
  238. BOOL fKey;
  239. BOOL fStopping; // True when finishing capture
  240. DWORD dw;
  241. WORD w;
  242. WORD wError; // Error String ID
  243. LPVIDEOHDR lpVidHdr;
  244. LPWAVEHDR lpWaveHdr;
  245. DWORD dwTimeToStop; // Lesser of MCI capture time or frame limit
  246. BOOL fTryToPaint = FALSE;
  247. HDC hdc;
  248. HPALETTE hpalT;
  249. RECT rcDrawRect;
  250. LONG lSize;
  251. statusUpdateStatus(lpcs, IDS_CAP_BEGIN); // Always the first message
  252. // Verify capture parameters
  253. if ((!lpcs->sCapParms.fMCIControl) ||
  254. (!lpcs->sCapParms.fStepMCIDevice))
  255. goto EarlyExit;
  256. lpcs->MCICaptureState = CAPMCI_STATE_Uninitialized;
  257. lpcs-> fCapturingNow = TRUE;
  258. lpcs-> fStepCapturingNow = TRUE;
  259. lpcs-> dwReturn = DV_ERR_OK;
  260. // If not 1 Meg. free, give it up!!!
  261. if (GetFreePhysicalMemory () < (1024L * 1024L)) {
  262. errorUpdateError (lpcs, IDS_CAP_OUTOFMEM);
  263. lpcs-> dwReturn = IDS_CAP_OUTOFMEM;
  264. goto EarlyExit;
  265. }
  266. statusUpdateStatus(lpcs, IDS_CAP_STAT_CAP_INIT);
  267. // Try painting the DIB only if Live window
  268. fTryToPaint = lpcs->fLiveWindow;
  269. if (fTryToPaint) {
  270. hdc = GetDC(lpcs->hwnd);
  271. SetWindowOrg(hdc, lpcs->ptScroll.x, lpcs->ptScroll.y);
  272. hpalT = DrawDibGetPalette (lpcs->hdd);
  273. if (hpalT)
  274. hpalT = SelectPalette( hdc, hpalT, FALSE);
  275. RealizePalette(hdc);
  276. if (lpcs-> fScale)
  277. GetClientRect (lpcs->hwnd, &rcDrawRect);
  278. else
  279. SetRect (&rcDrawRect, 0, 0, lpcs->dxBits, lpcs->dyBits);
  280. }
  281. // -------------------------------------------------------
  282. // When should capture stop?
  283. // -------------------------------------------------------
  284. // If using MCI, capture for the shorter of the MCI period,
  285. // or the capture limit
  286. if (lpcs->sCapParms.fLimitEnabled)
  287. dwTimeToStop = (DWORD) ((DWORD) 1000 * lpcs->sCapParms.wTimeLimit);
  288. else
  289. dwTimeToStop = (DWORD) -1L; // very large
  290. if (lpcs->sCapParms.fMCIControl) {
  291. // if MCI stop time not given, use lpcs->sCapParms.wTimeLimit
  292. if (lpcs->sCapParms.dwMCIStopTime == lpcs->sCapParms.dwMCIStartTime)
  293. lpcs->sCapParms.dwMCIStopTime = lpcs->sCapParms.dwMCIStartTime +
  294. (DWORD) ((DWORD)1000 * lpcs->sCapParms.wTimeLimit);
  295. dw = lpcs->sCapParms.dwMCIStopTime - lpcs->sCapParms.dwMCIStartTime;
  296. if (lpcs->sCapParms.fLimitEnabled)
  297. dwTimeToStop = min (dw, dwTimeToStop);
  298. else
  299. dwTimeToStop = dw;
  300. }
  301. //
  302. // never ever try to capture more than the index size!
  303. //
  304. if (lpcs->fCapturingToDisk) {
  305. dw = muldiv32(lpcs->sCapParms.dwIndexSize,
  306. lpcs->sCapParms.dwRequestMicroSecPerFrame,
  307. 1000l);
  308. dwTimeToStop = min (dw, dwTimeToStop);
  309. }
  310. fOK = FALSE; // Assume the worst
  311. if (MCIDeviceOpen (lpcs)) {
  312. if (MCIDeviceSetPosition (lpcs, lpcs->sCapParms.dwMCIStartTime))
  313. if (MCIDeviceStep (lpcs, TRUE))
  314. fOK = TRUE;
  315. }
  316. if (!fOK) {
  317. errorUpdateError (lpcs, IDS_CAP_MCI_CONTROL_ERROR);
  318. statusUpdateStatus(lpcs, NULL); // Clear status
  319. lpcs-> dwReturn = IDS_CAP_MCI_CONTROL_ERROR;
  320. goto EarlyExit;
  321. }
  322. // -------------------------------------------------------
  323. // Spatial and temporal averaging
  324. // -------------------------------------------------------
  325. // Frame Averaging, capture the same frame multiple times...
  326. lpcs->lpia = NULL;
  327. if (lpcs->sCapParms.wStepCaptureAverageFrames == 0)
  328. lpcs->sCapParms.wStepCaptureAverageFrames = 1;
  329. // Only allow averaging if an RGB format
  330. if (lpcs->lpBitsInfo->bmiHeader.biCompression != BI_RGB)
  331. lpcs->sCapParms.wStepCaptureAverageFrames = 1;
  332. // 2x Scaling
  333. lpcs->lpbmih2x = NULL;
  334. lpcs->VidHdr2x = lpcs->VidHdr; // Init the 2x copy
  335. if (lpcs->sCapParms.fStepCaptureAt2x &&
  336. lpcs->lpBitsInfo->bmiHeader.biCompression == BI_RGB) {
  337. lpcs->VidHdr2x.lpData = NULL;
  338. lpcs->lpbmih2x = (LPBITMAPINFOHEADER) GlobalAllocPtr (GHND,
  339. sizeof (BITMAPINFOHEADER) +
  340. 256 * sizeof (RGBQUAD));
  341. _fmemcpy (lpcs->lpbmih2x, lpcs->lpBitsInfo, sizeof (BITMAPINFOHEADER) +
  342. 256 * sizeof (RGBQUAD));
  343. // Try to force the driver into 2x mode
  344. lpcs->lpbmih2x->biHeight *= 2;
  345. lpcs->lpbmih2x->biWidth *= 2;
  346. lpcs->lpbmih2x->biSizeImage *= 4;
  347. if (!SendDriverFormat (lpcs, lpcs->lpbmih2x, sizeof (BITMAPINFOHEADER))) {
  348. // Success, allocate new bitspace
  349. lpcs->VidHdr2x.lpData = GlobalAllocPtr (GHND,
  350. lpcs->lpbmih2x->biSizeImage);
  351. lpcs->VidHdr2x.dwBufferLength = lpcs->lpbmih2x->biSizeImage;
  352. }
  353. // Something went wrong, no memory, or driver failed request
  354. // so revert back to original settings
  355. if (!lpcs->VidHdr2x.lpData) {
  356. SendDriverFormat (lpcs, (LPBITMAPINFOHEADER) lpcs->lpBitsInfo,
  357. sizeof (BITMAPINFOHEADER));
  358. lpcs->sCapParms.fStepCaptureAt2x = FALSE;
  359. lpcs->VidHdr2x = lpcs->VidHdr; // Back to the original settings
  360. }
  361. }
  362. else
  363. lpcs->sCapParms.fStepCaptureAt2x = FALSE;
  364. DPF (" StepCaptureAt2x = %d\r\n", (int) lpcs->sCapParms.fStepCaptureAt2x);
  365. //
  366. // If we're compressing while capturing, warm up the compressor
  367. //
  368. if (lpcs->CompVars.hic) {
  369. if (ICSeqCompressFrameStart(&lpcs->CompVars, lpcs->lpBitsInfo) == NULL) {
  370. // !!! We're in trouble here!
  371. dprintf("ICSeqCompressFrameStart failed !!!\n");
  372. lpcs-> dwReturn = IDS_CAP_COMPRESSOR_ERROR;
  373. errorUpdateError (lpcs, IDS_CAP_COMPRESSOR_ERROR);
  374. goto EarlyExit;
  375. }
  376. // Kludge, offset the lpBitsOut ptr
  377. // Compman allocates the compress buffer too large by
  378. // 2048 + 16 so we will still have room
  379. ((LPBYTE) lpcs->CompVars.lpBitsOut) += 8;
  380. }
  381. // No compression desired
  382. if (!lpcs->CompVars.hic)
  383. WinAssert(lpcs->CompVars.lpbiOut == NULL);
  384. // -------------------------------------------------------
  385. // Open the output file
  386. // -------------------------------------------------------
  387. if (lpcs->fCapturingToDisk) {
  388. if (!AVIFileInit(lpcs)) {
  389. lpcs-> dwReturn = IDS_CAP_FILE_OPEN_ERROR;
  390. errorUpdateError (lpcs, IDS_CAP_FILE_OPEN_ERROR);
  391. goto EarlyExit;
  392. }
  393. }
  394. /* Make sure the parent has been repainted */
  395. UpdateWindow(lpcs->hwnd);
  396. //
  397. // AVIInit will allocate sound buffers, but not video buffers
  398. // when performing step capture.
  399. //
  400. wError = AVIInit(lpcs);
  401. if (wError) {
  402. lpcs->sCapParms.fUsingDOSMemory = FALSE;
  403. wError = AVIInit(lpcs);
  404. }
  405. if (wError) {
  406. /* Error in initalization - return */
  407. errorUpdateError (lpcs, wError);
  408. AVIFini(lpcs);
  409. AVIFileFini(lpcs, TRUE /* fWroteJunkChunks */, TRUE /* fAbort */);
  410. statusUpdateStatus(lpcs, NULL); // Clear status
  411. lpcs-> dwReturn = wError;
  412. goto EarlyExit;
  413. }
  414. /* update the status, so the user knows how to stop */
  415. statusUpdateStatus(lpcs, IDS_CAP_SEQ_MSGSTOP);
  416. UpdateWindow(lpcs->hwnd);
  417. if (lpcs->sCapParms.fStepCaptureAt2x || (lpcs->sCapParms.wStepCaptureAverageFrames != 1)) {
  418. LPIAVERAGE FAR * lppia = (LPIAVERAGE FAR *) &lpcs->lpia;
  419. statusUpdateStatus (lpcs, IDS_CAP_STAT_PALETTE_BUILD);
  420. if (!iaverageInit (lppia, lpcs->lpBitsInfo, lpcs->hPalCurrent)) {
  421. lpcs-> dwReturn = IDS_CAP_OUTOFMEM;
  422. goto CompressFrameFailure;
  423. }
  424. statusUpdateStatus(lpcs, NULL);
  425. }
  426. DPF (" Averaging %d frames\r\n", lpcs->sCapParms.wStepCaptureAverageFrames);
  427. GetAsyncKeyState(lpcs->sCapParms.vKeyAbort);
  428. GetAsyncKeyState(VK_ESCAPE);
  429. GetAsyncKeyState(VK_LBUTTON);
  430. GetAsyncKeyState(VK_RBUTTON);
  431. // -------------------------------------------------------
  432. // MAIN VIDEO CAPTURE LOOP
  433. // -------------------------------------------------------
  434. fOK=TRUE; // Set FALSE on write errors
  435. fStopping = FALSE; // TRUE when we need to stop
  436. lpVidHdr = &lpcs->VidHdr;
  437. lpWaveHdr = lpcs->alpWaveHdr[lpcs->iNextWave];
  438. lpcs->MCICaptureState = CAPMCI_STATE_Initialized;
  439. lpcs->dwTimeElapsedMS = 0;
  440. // Move back to the starting position
  441. MCIDeviceSetPosition (lpcs, lpcs->sCapParms.dwMCIStartTime);
  442. MCIDevicePause (lpcs);
  443. // Where are we *really*
  444. MCIDeviceGetPosition (lpcs, &lpcs->dwMCIActualStartMS);
  445. // freeze video
  446. MCIDeviceFreeze(lpcs, TRUE);
  447. while (lpcs->MCICaptureState != CAPMCI_STATE_AllFini) {
  448. // -------------------------------------------------------
  449. // is there any reason to stop or change states
  450. // -------------------------------------------------------
  451. if (lpcs->sCapParms.vKeyAbort) {
  452. if (GetAsyncKeyState(lpcs->sCapParms.vKeyAbort & 0x00ff) & 0x0001) {
  453. fT = TRUE;
  454. if (lpcs->sCapParms.vKeyAbort & 0x8000) // Ctrl?
  455. fT = fT && (GetAsyncKeyState(VK_CONTROL) & 0x8000);
  456. if (lpcs->sCapParms.vKeyAbort & 0x4000) // Shift?
  457. fT = fT && (GetAsyncKeyState(VK_SHIFT) & 0x8000);
  458. fStopping = fT; // User aborts
  459. }
  460. }
  461. #if 0
  462. // Ignore Left mouse on MCI Capture!!!
  463. if (lpcs->sCapParms.fAbortLeftMouse)
  464. if (GetAsyncKeyState(VK_LBUTTON) & 0x0001)
  465. fStopping = TRUE; // User aborts
  466. #endif
  467. if (lpcs->sCapParms.fAbortRightMouse)
  468. if (GetAsyncKeyState(VK_RBUTTON) & 0x0001)
  469. fStopping = TRUE; // User aborts
  470. if (lpcs-> fAbortCapture) {
  471. fStopping = TRUE; // Somebody above wants us to quit
  472. }
  473. if (lpcs-> dwTimeElapsedMS > dwTimeToStop)
  474. fStopping = TRUE; // all done
  475. // -------------------------------------------------------
  476. // State machine
  477. // -------------------------------------------------------
  478. switch (lpcs-> MCICaptureState) {
  479. case CAPMCI_STATE_Initialized:
  480. // Begin video step capture
  481. DSTATUS(lpcs, "MCIState: Initialized");
  482. lpcs->MCICaptureState = CAPMCI_STATE_StartVideo;
  483. break;
  484. case CAPMCI_STATE_StartVideo:
  485. // Begin video step capture
  486. lpcs->dwTimeElapsedMS = 0;
  487. lpcs->MCICaptureState = CAPMCI_STATE_CapturingVideo;
  488. break;
  489. case CAPMCI_STATE_CapturingVideo:
  490. // In the state of capturing video
  491. if (lpcs-> fStopCapture || lpcs-> fAbortCapture)
  492. fStopping = TRUE;
  493. if (fStopping) {
  494. MCIDeviceGetPosition (lpcs, &lpcs->dwMCIActualEndMS);
  495. MCIDevicePause (lpcs);
  496. DSTATUS(lpcs, "MCIState: StoppingVideo");
  497. if (fOK && !lpcs-> fAbortCapture)
  498. lpcs->MCICaptureState = CAPMCI_STATE_VideoFini;
  499. else
  500. lpcs->MCICaptureState = CAPMCI_STATE_AllFini;
  501. lpcs-> fStopCapture = FALSE;
  502. lpcs-> fAbortCapture = FALSE;
  503. fStopping = FALSE;
  504. }
  505. break;
  506. case CAPMCI_STATE_VideoFini:
  507. // Wait for all buffers to be returned from the driver
  508. // Then move on to audio capture
  509. lpcs->MCICaptureState = CAPMCI_STATE_StartAudio;
  510. DSTATUS(lpcs, "MCIState: VideoFini");
  511. break;
  512. case CAPMCI_STATE_StartAudio:
  513. // If no audio, go to AllFini state
  514. if (!lpcs->sCapParms.fCaptureAudio || !fOK) {
  515. lpcs->MCICaptureState = CAPMCI_STATE_AllFini;
  516. break;
  517. }
  518. // Move back to the starting position
  519. MCIDeviceSetPosition (lpcs, lpcs->dwMCIActualStartMS);
  520. MCIDeviceGetPosition (lpcs, &lpcs->dwMCICurrentMS);
  521. DSTATUS(lpcs, "MCIState: StartAudio");
  522. MCIDevicePlay (lpcs);
  523. waveInStart(lpcs->hWaveIn);
  524. lpcs->MCICaptureState = CAPMCI_STATE_CapturingAudio;
  525. lpcs->dwTimeElapsedMS = 0;
  526. fStopping = FALSE;
  527. break;
  528. case CAPMCI_STATE_CapturingAudio:
  529. // In the state of capturing audio
  530. if (lpcs-> fStopCapture || lpcs-> fAbortCapture)
  531. fStopping = TRUE;
  532. MCIDeviceGetPosition (lpcs, &lpcs->dwMCICurrentMS);
  533. if (lpcs->dwMCICurrentMS + 100 > lpcs->dwMCIActualEndMS)
  534. fStopping = TRUE;
  535. if (fStopping) {
  536. waveInStop(lpcs->hWaveIn);
  537. MCIDevicePause (lpcs);
  538. waveInReset(lpcs->hWaveIn);
  539. lpcs->MCICaptureState = CAPMCI_STATE_AudioFini;
  540. }
  541. break;
  542. case CAPMCI_STATE_AudioFini:
  543. // While more audio buffers to process
  544. if (lpWaveHdr-> dwFlags & WHDR_DONE)
  545. break;
  546. lpcs->MCICaptureState = CAPMCI_STATE_AllFini;
  547. break;
  548. case CAPMCI_STATE_AllFini:
  549. DSTATUS(lpcs, "MCIState: AllFini");
  550. if (fOK)
  551. statusUpdateStatus(lpcs, IDS_CAP_STAT_CAP_FINI, lpcs->dwVideoChunkCount);
  552. else
  553. statusUpdateStatus(lpcs, IDS_CAP_RECORDING_ERROR2);
  554. break;
  555. }
  556. // -------------------------------------------------------
  557. // If we are in the video capture phase
  558. // -------------------------------------------------------
  559. if (lpcs->MCICaptureState == CAPMCI_STATE_CapturingVideo) {
  560. // if averaging...
  561. if (lpcs-> lpia) {
  562. int j;
  563. iaverageZero (lpcs-> lpia);
  564. // sum together a bunch of frames
  565. for (j = 0; j < (int)lpcs->sCapParms.wStepCaptureAverageFrames; j++) {
  566. videoFrame( lpcs-> hVideoIn, &lpcs-> VidHdr2x);
  567. // Shrink by 2x??
  568. if (lpcs-> sCapParms.fStepCaptureAt2x) {
  569. CrunchDIB(
  570. lpcs-> lpia, // image averaging structure
  571. (LPBITMAPINFOHEADER) lpcs-> lpbmih2x, // BITMAPINFO src
  572. (LPVOID) lpcs-> VidHdr2x.lpData, // input bits
  573. (LPBITMAPINFOHEADER) lpcs->lpBitsInfo, // BITMAPINFO dst
  574. (LPVOID) lpcs->VidHdr.lpData); // output bits
  575. }
  576. iaverageSum (lpcs-> lpia, lpcs->lpBits);
  577. }
  578. iaverageDivide (lpcs-> lpia, lpcs->lpBits);
  579. }
  580. // otherwise, not averaging, just get a frame
  581. else {
  582. videoFrame( lpcs-> hVideoIn, &lpcs->VidHdr);
  583. }
  584. if (lpcs->CallbackOnVideoFrame)
  585. (*(lpcs->CallbackOnVideoFrame)) (lpcs->hwnd, &lpcs->VidHdr);
  586. // Update the display
  587. InvalidateRect(lpcs->hwnd, NULL, TRUE);
  588. UpdateWindow(lpcs->hwnd);
  589. if (lpcs-> fCapturingToDisk) {
  590. if (!SingleFrameWrite (lpcs, lpVidHdr, &fKey, &lSize)) {
  591. fOK = FALSE;
  592. fStopping = TRUE;
  593. // "ERROR: Could not write to file."
  594. errorUpdateError(lpcs, IDS_CAP_FILE_WRITE_ERROR);
  595. }
  596. else {
  597. if (!IndexVideo(lpcs, lSize, fKey))
  598. fStopping = TRUE;
  599. }
  600. } // endif fCapturingToDisk
  601. // Warning: Kludge to create frame chunk count when net capture
  602. // follows.
  603. else
  604. lpcs->dwVideoChunkCount++;
  605. // if there is still more time, (or at least every 100 frames)
  606. // show status if we're not ending the capture
  607. if ((!fStopping) && (lpcs-> fCapturingToDisk) &&
  608. (lpcs->dwVideoChunkCount)) {
  609. // "Captured %ld frames (Dropped %ld) %d.%03d sec. Hit Escape to Stop"
  610. statusUpdateStatus(lpcs, IDS_CAP_STAT_VIDEOCURRENT,
  611. lpcs->dwVideoChunkCount, lpcs->dwFramesDropped,
  612. (int)(lpcs-> dwTimeElapsedMS/1000),
  613. (int)(lpcs-> dwTimeElapsedMS%1000)
  614. );
  615. } // endif next buffer not ready
  616. // Move the MCI source to the next capture point
  617. // unfreeze video
  618. MCIDeviceFreeze(lpcs, FALSE);
  619. for (;;) {
  620. MCIDeviceGetPosition (lpcs, &lpcs->dwMCICurrentMS);
  621. if (lpcs->dwMCICurrentMS > ((DWORD) (lpcs->dwMCIActualStartMS +
  622. muldiv32 (lpcs->dwVideoChunkCount,
  623. lpcs->sCapParms.dwRequestMicroSecPerFrame,
  624. 1000L))))
  625. break;
  626. MCIDeviceStep (lpcs, TRUE);
  627. }
  628. // freeze video
  629. MCIDeviceFreeze(lpcs, TRUE);
  630. lpcs-> dwTimeElapsedMS =
  631. lpcs->dwMCICurrentMS - lpcs->dwMCIActualStartMS;
  632. /* return the emptied buffer to the que */
  633. lpVidHdr->dwFlags &= ~VHDR_DONE;
  634. }
  635. if (lpcs-> CallbackOnYield) {
  636. // If the yield callback returns FALSE, abort
  637. if (!((*(lpcs->CallbackOnYield)) (lpcs->hwnd)))
  638. fStopping = TRUE;
  639. }
  640. if (lpcs->sCapParms.fYield) {
  641. MSG msg;
  642. if (PeekMessage(&msg,NULL,0,0,PM_REMOVE)) {
  643. // Kludge to get rid of timers from lpcs->hwnd
  644. if (msg.message == WM_TIMER && msg.hwnd == lpcs->hwnd)
  645. ;
  646. else {
  647. TranslateMessage(&msg);
  648. DispatchMessage(&msg);
  649. }
  650. }
  651. }
  652. // -------------------------------------------------------
  653. // Is audio buffer ready to be written?
  654. // -------------------------------------------------------
  655. if (lpcs->sCapParms.fCaptureAudio &&
  656. (lpcs-> MCICaptureState == CAPMCI_STATE_CapturingAudio ||
  657. lpcs-> MCICaptureState == CAPMCI_STATE_StartAudio ||
  658. lpcs-> MCICaptureState == CAPMCI_STATE_AudioFini)) {
  659. int iLastWave;
  660. //
  661. // we may need to yield for audio to get converted.
  662. //
  663. if (lpcs->fAudioYield)
  664. Yield();
  665. //
  666. // if all buffers are done, we have broke audio.
  667. //
  668. iLastWave = lpcs->iNextWave == 0 ?
  669. lpcs->iNumAudio -1 : lpcs->iNextWave-1;
  670. if (!fStopping &&
  671. lpcs->alpWaveHdr[iLastWave]->dwFlags & WHDR_DONE)
  672. lpcs->fAudioBreak = TRUE;
  673. w = lpcs->iNumAudio; // don't get stuck here forever...
  674. while (w && fOK && (lpWaveHdr-> dwFlags & WHDR_DONE)) {
  675. w--;
  676. if (lpWaveHdr-> dwBytesRecorded) {
  677. /* Chunk info is included in the wave data */
  678. /* Reset Chunk Size in buffer */
  679. ((LPRIFF)(lpWaveHdr->lpData))[-1].dwSize =
  680. lpWaveHdr-> dwBytesRecorded;
  681. if (lpcs-> CallbackOnWaveStream) {
  682. (*(lpcs->CallbackOnWaveStream)) (lpcs->hwnd, lpWaveHdr);
  683. }
  684. if (lpcs-> fCapturingToDisk) {
  685. if(!AVIWrite (lpcs, lpWaveHdr-> lpData - sizeof(RIFF),
  686. (lpWaveHdr-> dwBytesRecorded +
  687. sizeof (RIFF) + 1) & ~1L)) {
  688. fOK = FALSE;
  689. fStopping = TRUE;
  690. errorUpdateError (lpcs, IDS_CAP_FILE_WRITE_ERROR);
  691. } else {
  692. if (IndexAudio (lpcs, lpWaveHdr-> dwBytesRecorded))
  693. lpcs->dwWaveBytes += lpWaveHdr-> dwBytesRecorded;
  694. else
  695. fStopping = TRUE;
  696. }
  697. } // endif capturing to disk
  698. // Warning: Kludge to create wave chunk count when net capture
  699. // follows.
  700. else {
  701. lpcs->dwWaveChunkCount++;
  702. lpcs->dwWaveBytes += lpWaveHdr-> dwBytesRecorded;
  703. }
  704. } // endif dwBytesRecorded
  705. lpWaveHdr-> dwBytesRecorded = 0;
  706. lpWaveHdr-> dwFlags &= ~WHDR_DONE;
  707. /* return the emptied buffer to the que */
  708. if(waveInAddBuffer(lpcs->hWaveIn, lpWaveHdr, sizeof(WAVEHDR))) {
  709. fOK = FALSE;
  710. fStopping = TRUE;
  711. errorUpdateError(lpcs, IDS_CAP_WAVE_ADD_ERROR);
  712. }
  713. /* increment the next wave buffer pointer */
  714. if(++lpcs->iNextWave >= lpcs->iNumAudio)
  715. lpcs->iNextWave = 0;
  716. lpWaveHdr = lpcs->alpWaveHdr[lpcs->iNextWave];
  717. } // endwhile buffer available
  718. } // endif sound enabled
  719. } // end of forever
  720. CompressFrameFailure:
  721. iaverageFini (lpcs->lpia);
  722. // Switch back to the normal format
  723. if (lpcs->sCapParms.fStepCaptureAt2x) {
  724. SendDriverFormat (lpcs, (LPBITMAPINFOHEADER) lpcs->lpBitsInfo,
  725. sizeof (BITMAPINFOHEADER));
  726. GlobalFreePtr (lpcs->VidHdr2x.lpData);
  727. lpcs->VidHdr2x.lpData = NULL;
  728. }
  729. // And free the 2x memory
  730. if (lpcs->lpbmih2x) {
  731. GlobalFreePtr (lpcs->lpbmih2x);
  732. lpcs->lpbmih2x = NULL;
  733. }
  734. // -------------------------------------------------------
  735. // END OF MAIN CAPTURE LOOP
  736. // -------------------------------------------------------
  737. lpcs-> dwTimeElapsedMS = lpcs-> dwMCIActualEndMS - lpcs->dwMCIActualStartMS;
  738. /* eat any keys that have been pressed */
  739. while(GetKey(FALSE))
  740. ;
  741. AVIFini(lpcs); // does the Reset, and frees all buffers
  742. AVIFileFini(lpcs, FALSE /* fWroteJunkChunks */, FALSE /* fAbort */);
  743. /* Notify if there was an error while recording */
  744. if(!fOK) {
  745. errorUpdateError (lpcs, IDS_CAP_RECORDING_ERROR);
  746. }
  747. if (lpcs-> fCapturingToDisk) {
  748. if (lpcs->dwVideoChunkCount)
  749. dw = muldiv32(lpcs->dwVideoChunkCount,1000000,lpcs-> dwTimeElapsedMS);
  750. else
  751. dw = 0; // The muldiv32 doesn't give 0 if numerator is zero
  752. if(lpcs->sCapParms.fCaptureAudio) {
  753. // "Captured %d.%03d sec. %ld frames (%d dropped) (%d.%03d fps). %ld audio bytes (%d.%03d sps)"
  754. statusUpdateStatus(lpcs, IDS_CAP_STAT_VIDEOAUDIO,
  755. (WORD)(lpcs-> dwTimeElapsedMS/1000),
  756. (WORD)(lpcs-> dwTimeElapsedMS%1000),
  757. lpcs->dwVideoChunkCount,
  758. lpcs->dwFramesDropped,
  759. (WORD)(dw / 1000),
  760. (WORD)(dw % 1000),
  761. lpcs->dwWaveBytes,
  762. (WORD) lpcs->lpWaveFormat->nSamplesPerSec / 1000,
  763. (WORD) lpcs->lpWaveFormat->nSamplesPerSec % 1000);
  764. } else {
  765. // "Captured %d.%03d sec. %ld frames (%d dropped) (%d.%03d fps)."
  766. statusUpdateStatus(lpcs, IDS_CAP_STAT_VIDEOONLY,
  767. (WORD)(lpcs-> dwTimeElapsedMS/1000),
  768. (WORD)(lpcs-> dwTimeElapsedMS%1000),
  769. lpcs->dwVideoChunkCount,
  770. lpcs->dwFramesDropped,
  771. (WORD)(dw / 1000),
  772. (WORD)(dw % 1000));
  773. }
  774. } // endif capturing to disk (no warnings or errors if to net)
  775. // No frames captured, warn user that interrupts are probably not enabled.
  776. if (fOK && (lpcs->dwVideoChunkCount == 0)) {
  777. errorUpdateError (lpcs, IDS_CAP_NO_FRAME_CAP_ERROR);
  778. }
  779. // No audio captured, (but enabled), warn user audio card is hosed
  780. else if (fOK && lpcs->sCapParms.fCaptureAudio && (lpcs->dwWaveBytes == 0)) {
  781. errorUpdateError (lpcs, IDS_CAP_NO_AUDIO_CAP_ERROR);
  782. }
  783. // Audio underrun, inform user
  784. else if (fOK && lpcs->sCapParms.fCaptureAudio && lpcs->fAudioBreak) {
  785. errorUpdateError (lpcs, IDS_CAP_AUDIO_DROP_ERROR);
  786. }
  787. // If frames dropped, or changed capture rate, warn the user
  788. else if (fOK && lpcs->dwVideoChunkCount && lpcs->fCapturingToDisk) {
  789. // Warn user if dropped > 10% (default) of the frames
  790. if ((DWORD)100 * lpcs->dwFramesDropped / lpcs->dwVideoChunkCount >
  791. lpcs-> sCapParms.wPercentDropForError) {
  792. // "%d of %ld frames (%d.%03d\%) dropped during capture."
  793. errorUpdateError (lpcs, IDS_CAP_STAT_FRAMESDROPPED,
  794. lpcs->dwFramesDropped,
  795. lpcs->dwVideoChunkCount,
  796. (WORD)(muldiv32(lpcs->dwFramesDropped,10000,lpcs->dwVideoChunkCount)/100),
  797. (WORD)(muldiv32(lpcs->dwFramesDropped,10000,lpcs->dwVideoChunkCount)%100)
  798. );
  799. }
  800. }
  801. EarlyExit:
  802. //
  803. // If we were compressing while capturing, close it down
  804. //
  805. if (lpcs->CompVars.hic) {
  806. // Kludge, reset the lpBitsOut pointer
  807. if (lpcs->CompVars.lpBitsOut)
  808. ((LPBYTE) lpcs->CompVars.lpBitsOut) -= 8;
  809. ICSeqCompressFrameEnd(&lpcs->CompVars);
  810. }
  811. if (fTryToPaint) {
  812. if (hpalT)
  813. SelectPalette(hdc, hpalT, FALSE);
  814. ReleaseDC (lpcs->hwnd, hdc);
  815. }
  816. if (lpcs->sCapParms.fMCIControl)
  817. MCIDeviceClose (lpcs);
  818. // Let the user see where capture stopped
  819. if ((!lpcs->fLiveWindow) && (!lpcs->fOverlayWindow))
  820. videoFrame( lpcs->hVideoIn, &lpcs->VidHdr );
  821. InvalidateRect( lpcs->hwnd, NULL, TRUE);
  822. lpcs->fCapFileExists = (lpcs-> dwReturn == DV_ERR_OK);
  823. lpcs->fCapturingNow = FALSE;
  824. lpcs-> fStepCapturingNow = FALSE;
  825. statusUpdateStatus(lpcs, IDS_CAP_END); // Always the last message
  826. return;
  827. }
  828. 
  829.