Leaked source code of windows server 2003
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.

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