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.

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