Windows NT 4.0 source code leak
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.

1102 lines
37 KiB

4 years ago
  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. | TimeMSToSMPTE() - change milliseconds into SMPTE time |
  43. +--------------------------------------------------------------*/
  44. void FAR PASCAL TimeMSToSMPTE (DWORD dwMS, LPSTR 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. wsprintfA((LPSTR)lpTime, "%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. // Note: the next line is a HACK and relies on the knowledge of how
  642. // the compression buffer has been allocated.
  643. priff = ((LPRIFF)lpData) -1;
  644. priff->dwType = MAKEAVICKID(cktypeDIBbits, 0);
  645. priff->dwSize = dwBytesUsed;
  646. } else
  647. #endif // NEW_COMPMAN
  648. {
  649. lpData = lpVidHdr->lpData;
  650. dwBytesUsed = lpVidHdr->dwBytesUsed;
  651. }
  652. if (lpcs->CallbackOnVideoFrame)
  653. lpcs->CallbackOnVideoFrame(lpcs->hwnd, &lpcs->VidHdr);
  654. // Update the display
  655. InvalidateRect(lpcs->hwnd, NULL, TRUE);
  656. UpdateWindow(lpcs->hwnd);
  657. if (lpcs->fCaptureFlags & CAP_fCapturingToDisk) {
  658. UINT wError;
  659. BOOL bPending;
  660. if (!AVIWriteVideoFrame (lpcs,
  661. lpData,
  662. dwBytesUsed,
  663. fKeyFrame,
  664. (UINT)-1, 0, &wError, &bPending)) {
  665. fStopping = TRUE;
  666. if (wError)
  667. {
  668. fOK = FALSE;
  669. errorUpdateError(lpcs, wError);
  670. }
  671. assert (!bPending);
  672. }
  673. } // endif fCapturingToDisk
  674. // Warning: Kludge to create frame chunk count when net capture
  675. // follows.
  676. else
  677. lpcs->dwVideoChunkCount++;
  678. // if there is still more time, (or at least every 100 frames)
  679. // show status if we're not ending the capture
  680. if ((!fStopping) && (lpcs->fCaptureFlags & CAP_fCapturingToDisk) &&
  681. (lpcs->dwVideoChunkCount)) {
  682. // "Captured %ld frames (Dropped %ld) %d.%03d sec. Hit Escape to Stop"
  683. statusUpdateStatus(lpcs, IDS_CAP_STAT_VIDEOCURRENT,
  684. lpcs->dwVideoChunkCount, lpcs->dwFramesDropped,
  685. (int)(lpcs->dwTimeElapsedMS/1000),
  686. (int)(lpcs->dwTimeElapsedMS%1000)
  687. );
  688. } // endif next buffer not ready
  689. // Move the MCI source to the next capture point
  690. // unfreeze video
  691. MCIDeviceFreeze(lpcs, FALSE);
  692. {
  693. DWORD dwPrevMS = lpcs->dwMCICurrentMS;
  694. UINT nNonAdvanceCount = 0;
  695. DWORD dwTargetMS = lpcs->dwMCIActualStartMS
  696. + MulDiv (lpcs->dwVideoChunkCount,
  697. lpcs->sCapParms.dwRequestMicroSecPerFrame,
  698. 1000L);
  699. for (;;)
  700. {
  701. MCIDeviceGetPosition (lpcs, &lpcs->dwMCICurrentMS);
  702. if (lpcs->dwMCICurrentMS > dwTargetMS)
  703. break;
  704. if (lpcs->dwMCICurrentMS <= dwPrevMS)
  705. {
  706. if (++nNonAdvanceCount > 5)
  707. {
  708. fStopping = TRUE;
  709. break;
  710. }
  711. }
  712. else
  713. nNonAdvanceCount = 0;
  714. MCIDeviceStep (lpcs, TRUE);
  715. }
  716. }
  717. // freeze video
  718. MCIDeviceFreeze(lpcs, TRUE);
  719. lpcs->dwTimeElapsedMS =
  720. lpcs->dwMCICurrentMS - lpcs->dwMCIActualStartMS;
  721. /* return the emptied buffer to the que */
  722. lpVidHdr->dwFlags &= ~VHDR_DONE;
  723. }
  724. if (lpcs->CallbackOnYield) {
  725. // If the yield callback returns FALSE, abort
  726. if ( ! lpcs->CallbackOnYield(lpcs->hwnd))
  727. fStopping = TRUE;
  728. }
  729. if (lpcs->sCapParms.fYield) {
  730. MSG msg;
  731. if (PeekMessage(&msg,NULL,0,0,PM_REMOVE)) {
  732. // Kludge to get rid of timers from lpcs->hwnd
  733. if (msg.message == WM_TIMER && msg.hwnd == lpcs->hwnd)
  734. ;
  735. else {
  736. TranslateMessage(&msg);
  737. DispatchMessage(&msg);
  738. }
  739. }
  740. }
  741. // -------------------------------------------------------
  742. // Is audio buffer ready to be written?
  743. // -------------------------------------------------------
  744. if (lpcs->sCapParms.fCaptureAudio &&
  745. (lpcs->MCICaptureState == CAPMCI_STATE_CapturingAudio ||
  746. lpcs->MCICaptureState == CAPMCI_STATE_StartAudio ||
  747. lpcs->MCICaptureState == CAPMCI_STATE_AudioFini)) {
  748. int iLastWave;
  749. //
  750. // we may need to yield for audio to get converted.
  751. //
  752. if (lpcs->fAudioYield)
  753. Yield();
  754. //
  755. // if all buffers are done, we have broke audio.
  756. //
  757. iLastWave = lpcs->iNextWave == 0 ?
  758. lpcs->iNumAudio -1 : lpcs->iNextWave-1;
  759. if (!fStopping &&
  760. lpcs->alpWaveHdr[iLastWave]->dwFlags & WHDR_DONE)
  761. lpcs->fAudioBreak = TRUE;
  762. iNumAudio = lpcs->iNumAudio; // don't get stuck here forever...
  763. // We should probably assert that iNumAudio is >= 0...
  764. while (iNumAudio && fOK && (lpWaveHdr->dwFlags & WHDR_DONE)) {
  765. iNumAudio--;
  766. if (lpWaveHdr->dwBytesRecorded) {
  767. /* Chunk info is included in the wave data */
  768. /* Reset Chunk Size in buffer */
  769. ((LPRIFF)(lpWaveHdr->lpData))[-1].dwSize =
  770. lpWaveHdr->dwBytesRecorded;
  771. if (lpcs->CallbackOnWaveStream) {
  772. lpcs->CallbackOnWaveStream(lpcs->hwnd, lpWaveHdr);
  773. }
  774. if (lpcs->fCaptureFlags & CAP_fCapturingToDisk) {
  775. BOOL bPending = FALSE;
  776. UINT wError;
  777. if(!AVIWriteAudio(lpcs, lpWaveHdr, lpcs->iNextWave, &wError, &bPending)) {
  778. fStopping = TRUE;
  779. if (wError) {
  780. fOK = FALSE;
  781. errorUpdateError (lpcs, wError);
  782. }
  783. }
  784. } // endif capturing to disk
  785. // Warning: Kludge to create wave chunk count when net capture
  786. // follows.
  787. else {
  788. lpcs->dwWaveChunkCount++;
  789. lpcs->dwWaveBytes += lpWaveHdr->dwBytesRecorded;
  790. }
  791. } // endif dwBytesRecorded
  792. lpWaveHdr->dwBytesRecorded = 0;
  793. lpWaveHdr->dwFlags &= ~WHDR_DONE;
  794. /* return the emptied buffer to the device que */
  795. if(waveInAddBuffer(lpcs->hWaveIn, lpWaveHdr, sizeof(WAVEHDR))) {
  796. fOK = FALSE;
  797. fStopping = TRUE;
  798. errorUpdateError(lpcs, IDS_CAP_WAVE_ADD_ERROR);
  799. }
  800. /* increment the next wave buffer pointer */
  801. if(++lpcs->iNextWave >= lpcs->iNumAudio)
  802. lpcs->iNextWave = 0;
  803. lpWaveHdr = lpcs->alpWaveHdr[lpcs->iNextWave];
  804. } // endwhile buffer available
  805. } // endif sound enabled
  806. } // end of forever
  807. CompressFrameFailure:
  808. iaverageFini (lpcs->lpia);
  809. // Switch back to the normal format
  810. if (lpcs->sCapParms.fStepCaptureAt2x) {
  811. SendDriverFormat (lpcs, (LPBITMAPINFOHEADER) lpcs->lpBitsInfo,
  812. sizeof (BITMAPINFOHEADER));
  813. GlobalFreePtr (lpcs->VidHdr2x.lpData);
  814. lpcs->VidHdr2x.lpData = NULL;
  815. }
  816. // And free the 2x memory
  817. if (lpcs->lpbmih2x) {
  818. GlobalFreePtr (lpcs->lpbmih2x);
  819. lpcs->lpbmih2x = NULL;
  820. }
  821. // -------------------------------------------------------
  822. // END OF MAIN CAPTURE LOOP
  823. // -------------------------------------------------------
  824. lpcs->dwTimeElapsedMS = lpcs->dwMCIActualEndMS - lpcs->dwMCIActualStartMS;
  825. /* eat any keys that have been pressed */
  826. while(GetKey(FALSE))
  827. ;
  828. AVIFini(lpcs); // does the Reset, and frees all buffers
  829. AVIFileFini(lpcs, TRUE /* fWroteJunkChunks */, FALSE /* fAbort */);
  830. /* Notify if there was an error while recording */
  831. if(!fOK) {
  832. dprintf("Replacing error %x with %x (IDS_CAP_RECORDING_ERROR)\n", lpcs->dwReturn, IDS_CAP_RECORDING_ERROR);
  833. errorUpdateError (lpcs, IDS_CAP_RECORDING_ERROR);
  834. }
  835. if (lpcs->fCaptureFlags & CAP_fCapturingToDisk) {
  836. if (lpcs->dwVideoChunkCount)
  837. dw = MulDiv(lpcs->dwVideoChunkCount,1000000,lpcs->dwTimeElapsedMS);
  838. else
  839. dw = 0; // The MulDiv doesn't give 0 if numerator is zero
  840. if(lpcs->sCapParms.fCaptureAudio) {
  841. // "Captured %d.%03d sec. %ld frames (%d dropped) (%d.%03d fps). %ld audio bytes (%d.%03d sps)"
  842. statusUpdateStatus(lpcs, IDS_CAP_STAT_VIDEOAUDIO,
  843. (UINT)(lpcs->dwTimeElapsedMS/1000),
  844. (UINT)(lpcs->dwTimeElapsedMS%1000),
  845. lpcs->dwVideoChunkCount,
  846. lpcs->dwFramesDropped,
  847. (UINT)(dw / 1000),
  848. (UINT)(dw % 1000),
  849. lpcs->dwWaveBytes,
  850. (UINT) lpcs->lpWaveFormat->nSamplesPerSec / 1000,
  851. (UINT) lpcs->lpWaveFormat->nSamplesPerSec % 1000);
  852. } else {
  853. // "Captured %d.%03d sec. %ld frames (%d dropped) (%d.%03d fps)."
  854. statusUpdateStatus(lpcs, IDS_CAP_STAT_VIDEOONLY,
  855. (UINT)(lpcs->dwTimeElapsedMS/1000),
  856. (UINT)(lpcs->dwTimeElapsedMS%1000),
  857. lpcs->dwVideoChunkCount,
  858. lpcs->dwFramesDropped,
  859. (UINT)(dw / 1000),
  860. (UINT)(dw % 1000));
  861. }
  862. } // endif capturing to disk (no warnings or errors if to net)
  863. if (fOK) {
  864. // No frames captured, warn user that interrupts are probably not enabled.
  865. if (lpcs->dwVideoChunkCount == 0) {
  866. errorUpdateError (lpcs, IDS_CAP_NO_FRAME_CAP_ERROR);
  867. }
  868. // No audio captured, (but enabled), warn user audio card is hosed
  869. else if (lpcs->sCapParms.fCaptureAudio && (lpcs->dwWaveBytes == 0)) {
  870. errorUpdateError (lpcs, IDS_CAP_NO_AUDIO_CAP_ERROR);
  871. }
  872. // Audio underrun, inform user
  873. else if (lpcs->sCapParms.fCaptureAudio && lpcs->fAudioBreak) {
  874. if(lpcs->CompVars.hic) {
  875. errorUpdateError (lpcs, IDS_CAP_AUDIO_DROP_COMPERROR);
  876. } else {
  877. errorUpdateError (lpcs, IDS_CAP_AUDIO_DROP_ERROR);
  878. }
  879. }
  880. // If frames dropped, or changed capture rate, warn the user
  881. else if (lpcs->dwVideoChunkCount && (lpcs->fCaptureFlags & CAP_fCapturingToDisk)) {
  882. // Warn user if dropped > 10% (default) of the frames
  883. if ((DWORD)100 * lpcs->dwFramesDropped / lpcs->dwVideoChunkCount >
  884. lpcs->sCapParms.wPercentDropForError) {
  885. // "%d of %ld frames (%d.%03d\%) dropped during capture."
  886. errorUpdateError (lpcs, IDS_CAP_STAT_FRAMESDROPPED,
  887. lpcs->dwFramesDropped,
  888. lpcs->dwVideoChunkCount,
  889. (UINT)(MulDiv(lpcs->dwFramesDropped,10000,lpcs->dwVideoChunkCount)/100),
  890. (UINT)(MulDiv(lpcs->dwFramesDropped,10000,lpcs->dwVideoChunkCount)%100)/10
  891. );
  892. }
  893. }
  894. }
  895. EarlyExit:
  896. //
  897. // If we were compressing while capturing, close it down
  898. //
  899. #ifdef NEW_COMPMAN
  900. if (lpcs->CompVars.hic) {
  901. // Kludge, reset the lpBitsOut pointer
  902. if (lpcs->CompVars.lpBitsOut)
  903. ((LPBYTE) lpcs->CompVars.lpBitsOut) -= 8;
  904. ICSeqCompressFrameEnd(&lpcs->CompVars);
  905. }
  906. #endif
  907. if (fTryToPaint) {
  908. if (hpalT)
  909. SelectPalette(hdc, hpalT, FALSE);
  910. ReleaseDC (lpcs->hwnd, hdc);
  911. }
  912. if (lpcs->sCapParms.fMCIControl)
  913. MCIDeviceClose (lpcs);
  914. // Let the user see where capture stopped
  915. if ((!lpcs->fLiveWindow) && (!lpcs->fOverlayWindow))
  916. videoFrame( lpcs->hVideoIn, &lpcs->VidHdr );
  917. InvalidateRect( lpcs->hwnd, NULL, TRUE);
  918. lpcs->fCapFileExists = (lpcs->dwReturn == DV_ERR_OK);
  919. lpcs->fCaptureFlags &= ~CAP_fCapturingNow;
  920. lpcs->fCaptureFlags &= ~CAP_fStepCapturingNow;
  921. statusUpdateStatus(lpcs, IDS_CAP_END); // Always the last message
  922. return;
  923. }