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.

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