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.

2216 lines
72 KiB

  1. /****************************************************************************
  2. *
  3. * capavi.c
  4. *
  5. * Main video capture module.
  6. *
  7. * Microsoft Video for Windows Sample Capture Class
  8. *
  9. * Copyright (c) 1992, 1993 Microsoft Corporation. All Rights Reserved.
  10. *
  11. * You have a royalty-free right to use, modify, reproduce and
  12. * distribute the Sample Files (and/or any modified version) in
  13. * any way you find useful, provided that you agree that
  14. * Microsoft has no warranty obligations or liability for any
  15. * Sample Application Files which are modified.
  16. *
  17. ***************************************************************************/
  18. #include <windows.h>
  19. #include <windowsx.h>
  20. #include <mmsystem.h>
  21. #include <memory.h> // for _fmemset
  22. #include <msvideo.h>
  23. #include <drawdib.h>
  24. #include <mmreg.h>
  25. #include <mmddk.h>
  26. #include <msacm.h>
  27. #include <avifmt.h>
  28. #include "avicap.h"
  29. #include "avicapi.h"
  30. #include "time.h"
  31. time_t ltime;
  32. extern void NEAR PASCAL MemCopy(LPVOID, LPVOID, DWORD); // in memcopy.asm
  33. extern WORD FAR PASCAL SmartDrv(char chDrive, WORD w);
  34. extern WORD GetSizeOfWaveFormat (LPWAVEFORMATEX lpwf);
  35. /* dialog function prototype */
  36. LONG FAR PASCAL _export capseqDlgProc(HWND hwnd, unsigned msg, WORD wParam, LONG lParam);
  37. #ifdef _DEBUG
  38. #define DSTATUS(lpcs, sz) statusUpdateStatus(lpcs, IDS_CAP_INFO, (LPSTR) sz)
  39. #else
  40. #define DSTATUS(lpcs, sz)
  41. #endif
  42. ///////////////////////////////////////////////////////////////////////////
  43. // The index array is used to record the positions
  44. // of every chunk in the RIFF (avi) file.
  45. //
  46. // what this array is:
  47. //
  48. // each entry contains the size of the data
  49. // high order bits encode the type of data (audio / video)
  50. // and whether the video chunk is a key frame, dropped frame, etc.
  51. ///////////////////////////////////////////////////////////////////////////
  52. // The following are anded with the size in the index
  53. #define IS_AUDIO_CHUNK 0x80000000
  54. #define IS_KEYFRAME_CHUNK 0x40000000
  55. #define IS_DUMMY_CHUNK 0x20000000
  56. #define IS_LAST_DUMMY_CHUNK 0x10000000
  57. #define INDEX_MASK (IS_AUDIO_CHUNK | IS_KEYFRAME_CHUNK | IS_DUMMY_CHUNK | IS_LAST_DUMMY_CHUNK)
  58. // Allocate the index table
  59. // Returns: TRUE if index can be allocated
  60. BOOL InitIndex (LPCAPSTREAM lpcs)
  61. {
  62. lpcs->dwIndex = 0;
  63. WinAssert (lpcs->lpdwIndexStart == NULL);
  64. // Limit index size between 1 minute at 30fps and 3 hours at 30fps
  65. lpcs->sCapParms.dwIndexSize = max (lpcs->sCapParms.dwIndexSize, 1800);
  66. lpcs->sCapParms.dwIndexSize = min (lpcs->sCapParms.dwIndexSize, 324000L);
  67. dprintf("Max Index Size = %ld \n", lpcs->sCapParms.dwIndexSize);
  68. if (lpcs->hIndex = GlobalAlloc (GMEM_MOVEABLE,
  69. lpcs->sCapParms.dwIndexSize * sizeof (DWORD))) {
  70. if (lpcs->lpdwIndexEntry =
  71. lpcs->lpdwIndexStart =
  72. (DWORD _huge *)GlobalLock (lpcs->hIndex)) {
  73. GlobalPageLock (lpcs->hIndex);
  74. return TRUE; // Success
  75. }
  76. GlobalFree (lpcs->hIndex);
  77. }
  78. lpcs->hIndex = NULL;
  79. lpcs->lpdwIndexStart = NULL;
  80. return FALSE;
  81. }
  82. // Deallocate the index table
  83. void FiniIndex (LPCAPSTREAM lpcs)
  84. {
  85. if (lpcs->hIndex) {
  86. GlobalPageUnlock (lpcs->hIndex);
  87. if (lpcs->lpdwIndexStart)
  88. GlobalUnlock (lpcs->hIndex);
  89. GlobalFree (lpcs->hIndex);
  90. }
  91. lpcs->hIndex = NULL;
  92. lpcs->lpdwIndexStart = NULL;
  93. }
  94. // Add an index entry for a video frame
  95. // dwSize is the size of data ONLY, not including the chunk or junk
  96. // Returns: TRUE if index space is not exhausted
  97. BOOL IndexVideo (LPCAPSTREAM lpcs, DWORD dwSize, BOOL bKeyFrame)
  98. {
  99. BOOL fOK = lpcs->dwIndex < lpcs->sCapParms.dwIndexSize;
  100. if (fOK) {
  101. *lpcs->lpdwIndexEntry++ = dwSize | (bKeyFrame ? IS_KEYFRAME_CHUNK : 0);
  102. lpcs->dwIndex++;
  103. lpcs->dwVideoChunkCount++;
  104. }
  105. return (fOK);
  106. }
  107. // Add an index entry for an audio buffer
  108. // dwSize is the size of data ONLY, not including the chunk or junk
  109. // Returns: TRUE if index space is not exhausted
  110. BOOL IndexAudio (LPCAPSTREAM lpcs, DWORD dwSize)
  111. {
  112. BOOL fOK = lpcs->dwIndex < lpcs->sCapParms.dwIndexSize;
  113. if (fOK) {
  114. *lpcs->lpdwIndexEntry++ = dwSize | IS_AUDIO_CHUNK;
  115. lpcs->dwIndex++;
  116. lpcs->dwWaveChunkCount++;
  117. }
  118. return (fOK);
  119. }
  120. // Write out the index at the end of the capture file.
  121. // The single frame capture methods do not append
  122. // JunkChunks! Audio chunks do not have junk appended.
  123. BOOL WriteIndex (LPCAPSTREAM lpcs, BOOL fJunkChunkWritten)
  124. {
  125. BOOL fChunkIsAudio;
  126. BOOL fChunkIsKeyFrame;
  127. BOOL fChunkIsDummy;
  128. BOOL fChunkIsLastDummy;
  129. DWORD dwIndex;
  130. DWORD dw;
  131. DWORD dwDummySize;
  132. DWORD dwJunk;
  133. DWORD off;
  134. AVIINDEXENTRY avii;
  135. MMCKINFO ck;
  136. DWORD _huge *lpdw;
  137. if (lpcs->dwIndex > lpcs->sCapParms.dwIndexSize)
  138. return TRUE;
  139. off = lpcs->dwAVIHdrSize;
  140. ck.cksize = 0;
  141. ck.ckid = ckidAVINEWINDEX;
  142. ck.fccType = 0;
  143. if (mmioCreateChunk(lpcs->hmmio,&ck,0))
  144. return FALSE;
  145. lpdw = lpcs->lpdwIndexStart;
  146. for (dwIndex= 0; dwIndex< lpcs->dwIndex; dwIndex++) {
  147. dw = *lpdw++;
  148. fChunkIsAudio = (BOOL) ((dw & IS_AUDIO_CHUNK) != 0);
  149. fChunkIsKeyFrame = (BOOL) ((dw & IS_KEYFRAME_CHUNK) != 0);
  150. fChunkIsDummy = (BOOL) ((dw & IS_DUMMY_CHUNK) != 0);
  151. fChunkIsLastDummy = (BOOL) ((dw & IS_LAST_DUMMY_CHUNK) != 0);
  152. dw &= ~(INDEX_MASK);
  153. if (fChunkIsAudio) {
  154. avii.ckid = MAKEAVICKID(cktypeWAVEbytes, 1);
  155. avii.dwFlags = 0;
  156. } else {
  157. if (lpcs->lpBitsInfo->bmiHeader.biCompression == BI_RLE8)
  158. avii.ckid = MAKEAVICKID(cktypeDIBcompressed, 0);
  159. else
  160. avii.ckid = MAKEAVICKID(cktypeDIBbits, 0);
  161. avii.dwFlags = fChunkIsKeyFrame ? AVIIF_KEYFRAME : 0;
  162. }
  163. avii.dwChunkLength = dw;
  164. avii.dwChunkOffset = off;
  165. if (mmioWrite(lpcs->hmmio, (LPVOID)&avii, sizeof(avii)) != sizeof(avii))
  166. return FALSE;
  167. dw += sizeof (RIFF);
  168. off += dw;
  169. // ooh, getting messy. We know that dummy chunks come in a group
  170. // (1 or more) and are always terminated by a IS_LAST_DUMMY_CHUNK flag.
  171. // only the last one gets junk append to round out to 2K
  172. if (fChunkIsDummy) {
  173. dwDummySize += sizeof(RIFF);
  174. if (!fChunkIsLastDummy)
  175. continue;
  176. else
  177. dw = dwDummySize; // total size of all dummy entries in group
  178. }
  179. else
  180. dwDummySize = 0;
  181. if (fJunkChunkWritten & !fChunkIsAudio) {
  182. // If a Junk chunk was appended, move past it
  183. if (dw % lpcs->sCapParms.wChunkGranularity) {
  184. dwJunk = lpcs->sCapParms.wChunkGranularity - (dw % lpcs->sCapParms.wChunkGranularity);
  185. if (dwJunk < sizeof (RIFF))
  186. off += lpcs->sCapParms.wChunkGranularity + dwJunk;
  187. else
  188. off += dwJunk;
  189. }
  190. }
  191. if (off & 1)
  192. off++;
  193. }
  194. if (mmioAscend(lpcs->hmmio, &ck, 0))
  195. return FALSE;
  196. return TRUE;
  197. }
  198. // Allocate DOS memory for faster disk writes
  199. LPVOID NEAR PASCAL AllocDosMem (DWORD dw)
  200. {
  201. HANDLE h;
  202. if (h = LOWORD (GlobalDosAlloc(dw)))
  203. return (GlobalLock (h));
  204. return NULL;
  205. }
  206. // General purpose memory allocator
  207. LPVOID NEAR PASCAL AllocMem (DWORD dw, BOOL fUseDOSMemory)
  208. {
  209. #if 0
  210. if (fUseDOSMemory)
  211. return AllocDosMem(dw);
  212. #endif
  213. return GlobalAllocPtr (GMEM_MOVEABLE, dw);
  214. }
  215. void NEAR PASCAL FreeMem(LPVOID p)
  216. {
  217. GlobalFreePtr(p);
  218. }
  219. #pragma optimize ("", off)
  220. DWORD GetFreePhysicalMemory(void)
  221. {
  222. DWORD adw[ 0x30 / sizeof(DWORD) ];
  223. WORD fFail;
  224. //
  225. // if standard mode just ask KERNEL how much memory is free
  226. //
  227. // if enhanced mode, call DPMI and find out how much *real*
  228. // memory is free.
  229. //
  230. if (GetWinFlags() & WF_STANDARD)
  231. {
  232. return GetFreeSpace(0);
  233. }
  234. else _asm
  235. {
  236. mov ax, 0500h
  237. push ss
  238. pop es
  239. lea di, word ptr adw
  240. int 31h
  241. sbb ax, ax
  242. mov fFail, ax
  243. }
  244. if (fFail)
  245. return (0l);
  246. return (adw[2] * 4096);
  247. }
  248. #pragma optimize ("", on)
  249. /*
  250. * CalcWaveBufferSize - Figure out how large to make the wave buffers
  251. * a. At least .5 seconds
  252. * b. But not less than 10K, (else capture frmae rate suffers)
  253. * c. A multiple of lpcs->sCapParms.wChunkGranularity
  254. */
  255. DWORD CalcWaveBufferSize (LPCAPSTREAM lpcs)
  256. {
  257. DWORD dw;
  258. if (!lpcs-> lpWaveFormat)
  259. return 0L;
  260. // at least .5 second
  261. dw = (DWORD) lpcs->lpWaveFormat->nChannels *
  262. (DWORD) lpcs->lpWaveFormat->nSamplesPerSec *
  263. (lpcs->lpWaveFormat->wBitsPerSample / 8) / 2L;
  264. dw -= dw % lpcs->sCapParms.wChunkGranularity;
  265. dw = max ((1024L * 10), dw); // at least 10K
  266. // dprintf("Wave buffer size = %ld \n", dw);
  267. return dw;
  268. }
  269. static BOOL IsWaveInDeviceMapped(HWAVEIN hWaveIn)
  270. {
  271. DWORD err;
  272. DWORD dw;
  273. err = waveInMessage(hWaveIn,
  274. WIDM_MAPPER_STATUS,
  275. WAVEIN_MAPPER_STATUS_MAPPED,
  276. (DWORD)(LPVOID)&dw);
  277. return err == 0 && dw != 0;
  278. }
  279. // ****************************************************************
  280. // ******************** Capture File Routines *********************
  281. // ****************************************************************
  282. /*
  283. * AVIFileInit
  284. *
  285. * Perform all initialization required to write a capture file.
  286. *
  287. * We take a slightly strange approach: We don't write
  288. * out the header until we're done capturing. For now,
  289. * we just seek 2K into the file, which is where all of
  290. * the real data will go.
  291. *
  292. * When we're done, we'll come back and write out the header,
  293. * because then we'll know all of the values we need.
  294. *
  295. * Also allocate and init the index.
  296. */
  297. BOOL AVIFileInit(LPCAPSTREAM lpcs)
  298. {
  299. #define TEMP_BUFF_SIZE 128
  300. LONG l;
  301. char ach[TEMP_BUFF_SIZE];
  302. LPBITMAPINFO lpBitsInfoOut; // Possibly compressed output format
  303. /* No special video format given -- use the default */
  304. if (lpcs->CompVars.hic == NULL)
  305. lpBitsInfoOut = lpcs->lpBitsInfo;
  306. else
  307. lpBitsInfoOut = lpcs->CompVars.lpbiOut;
  308. WinAssert (lpcs->hmmio == NULL); // Should never have a file handle on entry
  309. /* if the capture file has not been set then set it now */
  310. if (!(*lpcs->achFile)){
  311. // if (!fileSetCapFile())
  312. goto INIT_FILE_OPEN_ERROR;
  313. }
  314. /* we have a capture file, open it and set it up */
  315. lpcs->hmmio = mmioOpen(lpcs->achFile, NULL, MMIO_WRITE);
  316. if (!lpcs->hmmio) {
  317. /* try and create */
  318. lpcs->hmmio = mmioOpen(lpcs->achFile, NULL, MMIO_CREATE | MMIO_WRITE);
  319. if (!lpcs->hmmio) {
  320. goto INIT_FILE_OPEN_ERROR;
  321. }
  322. }
  323. /* pre-read the file */
  324. l = mmioSeek( lpcs->hmmio, 0L, SEEK_END );
  325. while( l > 0 ) {
  326. l = mmioSeek( lpcs->hmmio, -min(l, 50000L), SEEK_CUR );
  327. mmioRead( lpcs->hmmio, ach, sizeof(ach) );
  328. }
  329. /* Seek to 2K (or multiple of 2K), where we're going to write our data.
  330. ** later, we'll come back and fill in the file.
  331. */
  332. // l is zero for standard wave and video formats
  333. l = (GetSizeOfWaveFormat ((LPWAVEFORMATEX) lpcs->lpWaveFormat) -
  334. sizeof (PCMWAVEFORMAT)) +
  335. (lpBitsInfoOut->bmiHeader.biSize -
  336. sizeof (BITMAPINFOHEADER));
  337. // (2K + size of wave and video stream headers) rounded to next 2K
  338. lpcs->dwAVIHdrSize = AVI_HEADERSIZE +
  339. (((lpcs->cbInfoChunks + l + lpcs->sCapParms.wChunkGranularity - 1)
  340. / lpcs->sCapParms.wChunkGranularity) * lpcs->sCapParms.wChunkGranularity);
  341. dprintf("AVIHdrSize = %ld \n", lpcs->dwAVIHdrSize);
  342. mmioSeek(lpcs->hmmio, lpcs->dwAVIHdrSize, SEEK_SET);
  343. if (!InitIndex (lpcs)) // do all Index allocations
  344. mmioClose (lpcs->hmmio, 0);
  345. lpcs->dwVideoChunkCount = 0;
  346. lpcs->dwWaveChunkCount = 0;
  347. INIT_FILE_OPEN_ERROR:
  348. return (lpcs->hmmio != NULL);
  349. }
  350. /*
  351. * AVIFileFini
  352. *
  353. * Write out the index, deallocate the index, and close the file.
  354. *
  355. */
  356. BOOL AVIFileFini (LPCAPSTREAM lpcs, BOOL fWroteJunkChunks, BOOL fAbort)
  357. {
  358. MMCKINFO ckRiff;
  359. MMCKINFO ckList;
  360. MMCKINFO ckStream;
  361. MMCKINFO ck;
  362. int i;
  363. DWORD dw;
  364. AVIStreamHeader strhdr;
  365. DWORD dwDataEnd;
  366. BOOL fRet = TRUE;
  367. RGBQUAD argbq[256];
  368. MainAVIHeader aviHdr;
  369. BOOL fSound = lpcs->sCapParms.fCaptureAudio;
  370. LPBITMAPINFO lpBitsInfoOut; // Possibly compressed output format
  371. /* No special video format given -- use the default */
  372. if (lpcs->CompVars.hic == NULL)
  373. lpBitsInfoOut = lpcs->lpBitsInfo;
  374. else
  375. lpBitsInfoOut = lpcs->CompVars.lpbiOut;
  376. if (lpcs->hmmio == NULL) // This can be called even though never opened
  377. return FALSE;
  378. if (fAbort)
  379. goto FileError;
  380. if (!lpcs->dwWaveBytes)
  381. fSound = FALSE;
  382. dwDataEnd = mmioSeek(lpcs->hmmio, 0, SEEK_CUR);
  383. /* Seek to beginning of file, so we can write the header. */
  384. mmioSeek(lpcs->hmmio, 0, SEEK_SET);
  385. DSTATUS(lpcs, "Writing AVI header");
  386. /* Create RIFF chunk */
  387. ckRiff.cksize = 0;
  388. ckRiff.fccType = formtypeAVI;
  389. if(mmioCreateChunk(lpcs->hmmio,&ckRiff,MMIO_CREATERIFF)) {
  390. goto FileError;
  391. }
  392. /* Create header list */
  393. ckList.cksize = 0;
  394. ckList.fccType = listtypeAVIHEADER;
  395. if(mmioCreateChunk(lpcs->hmmio,&ckList,MMIO_CREATELIST)) {
  396. goto FileError;
  397. }
  398. /* Create AVI header chunk */
  399. ck.cksize = sizeof(MainAVIHeader);
  400. ck.ckid = ckidAVIMAINHDR;
  401. if(mmioCreateChunk(lpcs->hmmio,&ck,0)) {
  402. goto FileError;
  403. }
  404. lpcs->dwAVIHdrPos = ck.dwDataOffset;
  405. /* Calculate AVI header info */
  406. _fmemset(&aviHdr, 0, sizeof(aviHdr));
  407. if (fSound && lpcs->dwVideoChunkCount) {
  408. /* HACK HACK */
  409. /* Set rate that was captured based on length of audio data */
  410. aviHdr.dwMicroSecPerFrame = (DWORD)
  411. ((double)lpcs->dwWaveBytes * 1000000. /
  412. ((double)lpcs->lpWaveFormat->nAvgBytesPerSec *
  413. lpcs->dwVideoChunkCount + 0.5));
  414. } else {
  415. aviHdr.dwMicroSecPerFrame = lpcs->sCapParms.dwRequestMicroSecPerFrame;
  416. }
  417. lpcs->dwActualMicroSecPerFrame = aviHdr.dwMicroSecPerFrame;
  418. aviHdr.dwMaxBytesPerSec = (DWORD) muldiv32 (lpBitsInfoOut->bmiHeader.biSizeImage,
  419. 1000000,
  420. lpcs->sCapParms.dwRequestMicroSecPerFrame) +
  421. (fSound ? lpcs->lpWaveFormat->nAvgBytesPerSec : 0);
  422. aviHdr.dwPaddingGranularity = 0L;
  423. aviHdr.dwFlags = AVIF_WASCAPTUREFILE | AVIF_HASINDEX;
  424. aviHdr.dwStreams = fSound ? 2 : 1;
  425. aviHdr.dwTotalFrames = lpcs->dwVideoChunkCount;
  426. aviHdr.dwInitialFrames = 0L;
  427. aviHdr.dwSuggestedBufferSize = 0L;
  428. aviHdr.dwWidth = lpBitsInfoOut->bmiHeader.biWidth;
  429. aviHdr.dwHeight = lpBitsInfoOut->bmiHeader.biHeight;
  430. // The following were set for all versions before Chicago Beta2
  431. // They are now listed as reserved...
  432. // aviHdr.dwRate = 1000000L;
  433. // aviHdr.dwScale = aviHdr.dwMicroSecPerFrame;
  434. // aviHdr.dwStart = 0L;
  435. // aviHdr.dwLength = lpcs->dwVideoChunkCount;
  436. /* Write AVI header info */
  437. if(mmioWrite(lpcs->hmmio, (LPSTR)&aviHdr, sizeof(aviHdr)) !=
  438. sizeof(aviHdr)) {
  439. goto FileError;
  440. }
  441. if(mmioAscend(lpcs->hmmio, &ck, 0)) {
  442. goto FileError;
  443. }
  444. DSTATUS(lpcs, "Writing AVI Stream header");
  445. /* Create stream header list */
  446. ckStream.cksize = 0;
  447. ckStream.fccType = listtypeSTREAMHEADER;
  448. if(mmioCreateChunk(lpcs->hmmio,&ckStream,MMIO_CREATELIST)) {
  449. goto FileError;
  450. }
  451. _fmemset(&strhdr, 0, sizeof(strhdr));
  452. strhdr.fccType = streamtypeVIDEO;
  453. if (lpcs->CompVars.hic)
  454. strhdr.fccHandler = lpcs->CompVars.fccHandler;
  455. else
  456. strhdr.fccHandler = lpBitsInfoOut->bmiHeader.biCompression;
  457. // A bit of history...
  458. // In VFW 1.0, we set fccHandler to 0 for BI_RLE8 formats
  459. // as a kludge to make Mplayer and Videdit play the files.
  460. // Just prior to 1.1 release, we found this broke Premiere,
  461. // so now (after AVICAP beta is on Compuserve), we change the
  462. // fccHandler to "MRLE". Just ask Todd...
  463. // And now, at RC1, we change it again to "RLE ", Just ask Todd...
  464. if (strhdr.fccHandler == BI_RLE8)
  465. strhdr.fccHandler = mmioFOURCC('R', 'L', 'E', ' ');
  466. strhdr.dwFlags = 0L;
  467. strhdr.wPriority = 0L;
  468. strhdr.wLanguage = 0L;
  469. strhdr.dwInitialFrames = 0L;
  470. strhdr.dwScale = aviHdr.dwMicroSecPerFrame;
  471. strhdr.dwRate = 1000000L;
  472. strhdr.dwStart = 0L;
  473. strhdr.dwLength = lpcs->dwVideoChunkCount; /* Needs to get filled in! */
  474. strhdr.dwQuality = (DWORD) -1L; /* !!! ICQUALITY_DEFAULT */
  475. strhdr.dwSampleSize = 0L;
  476. ck.ckid = ckidSTREAMHEADER;
  477. if(mmioCreateChunk(lpcs->hmmio,&ck,0)) {
  478. goto FileError;
  479. }
  480. /* Write stream header data */
  481. if(mmioWrite(lpcs->hmmio, (LPSTR)&strhdr, sizeof(strhdr)) != sizeof(strhdr)) {
  482. goto FileError;
  483. }
  484. if(mmioAscend(lpcs->hmmio, &ck, 0)) {
  485. goto FileError;
  486. }
  487. /*
  488. ** !!! dont write palette for full color?
  489. */
  490. if (lpBitsInfoOut->bmiHeader.biBitCount > 8)
  491. lpBitsInfoOut->bmiHeader.biClrUsed = 0;
  492. /* Create DIB header chunk */
  493. ck.cksize = lpBitsInfoOut->bmiHeader.biSize +
  494. lpBitsInfoOut->bmiHeader.biClrUsed *
  495. sizeof(RGBQUAD);
  496. ck.ckid = ckidSTREAMFORMAT;
  497. if(mmioCreateChunk(lpcs->hmmio,&ck,0)) {
  498. goto FileError;
  499. }
  500. /* Write DIB header data */
  501. if(mmioWrite(lpcs->hmmio, (LPSTR)&lpBitsInfoOut->bmiHeader,
  502. lpBitsInfoOut->bmiHeader.biSize) !=
  503. (LONG) lpBitsInfoOut->bmiHeader.biSize) {
  504. goto FileError;
  505. }
  506. if (lpBitsInfoOut->bmiHeader.biClrUsed > 0) {
  507. /* Get Palette info */
  508. if(GetPaletteEntries(lpcs->hPalCurrent, 0,
  509. (WORD) lpBitsInfoOut->bmiHeader.biClrUsed,
  510. (LPPALETTEENTRY) argbq) !=
  511. (WORD)lpBitsInfoOut->bmiHeader.biClrUsed) {
  512. goto FileError;
  513. }
  514. for(i = 0; i < (int) lpBitsInfoOut->bmiHeader.biClrUsed; i++)
  515. SWAP(argbq[i].rgbRed, argbq[i].rgbBlue);
  516. /* Write Palette Info */
  517. dw = sizeof(RGBQUAD) * lpBitsInfoOut->bmiHeader.biClrUsed;
  518. if (mmioWrite(lpcs->hmmio, (LPSTR)argbq, dw) != (long)dw) {
  519. goto FileError;
  520. }
  521. }
  522. if(mmioAscend(lpcs->hmmio, &ck, 0)) {
  523. goto FileError;
  524. }
  525. // ADD FOURCC stuff here!!! for Video stream
  526. /* Ascend out of stream header */
  527. if(mmioAscend(lpcs->hmmio, &ckStream, 0)) {
  528. goto FileError;
  529. }
  530. /* If sound is enabled, then write WAVE header */
  531. if(fSound) {
  532. /* Create stream header list */
  533. ckStream.cksize = 0;
  534. ckStream.fccType = listtypeSTREAMHEADER;
  535. if(mmioCreateChunk(lpcs->hmmio,&ckStream,MMIO_CREATELIST)) {
  536. goto FileError;
  537. }
  538. _fmemset(&strhdr, 0, sizeof(strhdr));
  539. strhdr.fccType = streamtypeAUDIO;
  540. strhdr.fccHandler = 0L;
  541. strhdr.dwFlags = 0L;
  542. strhdr.wPriority = 0L;
  543. strhdr.wLanguage = 0L;
  544. strhdr.dwInitialFrames = 0L;
  545. strhdr.dwScale = lpcs->lpWaveFormat->nBlockAlign;
  546. strhdr.dwRate = lpcs->lpWaveFormat->nAvgBytesPerSec;
  547. strhdr.dwStart = 0L;
  548. strhdr.dwLength = lpcs->dwWaveBytes /
  549. lpcs->lpWaveFormat->nBlockAlign;
  550. strhdr.dwQuality = (DWORD)-1L; /* !!! ICQUALITY_DEFAULT */
  551. strhdr.dwSampleSize = lpcs->lpWaveFormat->nBlockAlign;
  552. ck.ckid = ckidSTREAMHEADER;
  553. if(mmioCreateChunk(lpcs->hmmio,&ck,0)) {
  554. goto FileError;
  555. }
  556. if(mmioWrite(lpcs->hmmio, (LPSTR)&strhdr, sizeof(strhdr)) != sizeof(strhdr)) {
  557. goto FileError;
  558. }
  559. if(mmioAscend(lpcs->hmmio, &ck, 0)) {
  560. goto FileError;
  561. }
  562. ck.cksize = (LONG) GetSizeOfWaveFormat ((LPWAVEFORMATEX) lpcs->lpWaveFormat);
  563. ck.ckid = ckidSTREAMFORMAT;
  564. if(mmioCreateChunk(lpcs->hmmio,&ck,0)) {
  565. goto FileError;
  566. }
  567. /* Write WAVE header info */
  568. if(mmioWrite(lpcs->hmmio, (LPSTR)lpcs->lpWaveFormat, ck.cksize) != (LONG) ck.cksize) {
  569. goto FileError;
  570. }
  571. if(mmioAscend(lpcs->hmmio, &ck, 0)) {
  572. goto FileError;
  573. }
  574. /* Ascend out of stream header */
  575. if(mmioAscend(lpcs->hmmio, &ckStream, 0)) {
  576. goto FileError;
  577. }
  578. }
  579. // ADD FOURCC stuff here!!! for entire file
  580. DSTATUS(lpcs, "Writing Info chunks");
  581. if (lpcs->lpInfoChunks) {
  582. DSTATUS(lpcs, "Writing Info chunks");
  583. if (mmioWrite (lpcs->hmmio, lpcs->lpInfoChunks, lpcs->cbInfoChunks) !=
  584. lpcs->cbInfoChunks)
  585. goto FileError;
  586. }
  587. /* ascend from the Header list */
  588. if(mmioAscend(lpcs->hmmio, &ckList, 0)) {
  589. goto FileError;
  590. }
  591. ck.ckid = ckidAVIPADDING;
  592. if(mmioCreateChunk(lpcs->hmmio,&ck,0)) {
  593. goto FileError;
  594. }
  595. mmioSeek(lpcs->hmmio, lpcs->dwAVIHdrSize - 3 * sizeof(DWORD), SEEK_SET);
  596. if(mmioAscend(lpcs->hmmio, &ck, 0)) {
  597. goto FileError;
  598. }
  599. DSTATUS(lpcs, "Writing Movie LIST");
  600. /* Start the movi list */
  601. ckList.cksize = 0;
  602. ckList.fccType = listtypeAVIMOVIE;
  603. if(mmioCreateChunk(lpcs->hmmio,&ckList,MMIO_CREATELIST)) {
  604. goto FileError;
  605. }
  606. // Force the chunk to end on the next word boundary
  607. mmioSeek(lpcs->hmmio, dwDataEnd + (dwDataEnd & 1L), SEEK_SET);
  608. /* Ascend out of the movi list and the RIFF chunk so that */
  609. /* the sizes can be fixed */
  610. mmioAscend(lpcs->hmmio, &ckList, 0);
  611. /*
  612. ** Now write index out!
  613. */
  614. DSTATUS(lpcs, "Writing Index...");
  615. WriteIndex(lpcs, fWroteJunkChunks);
  616. lpcs->fFileCaptured = TRUE; // we got a good file, allow editing of it
  617. goto Success;
  618. FileError:
  619. lpcs->fFileCaptured = fRet = FALSE; // bogus file - no editing allowed
  620. Success:
  621. DSTATUS(lpcs, "Freeing Index...");
  622. FiniIndex (lpcs);
  623. mmioAscend(lpcs->hmmio, &ckRiff, 0);
  624. mmioSeek(lpcs->hmmio, 0, SEEK_END);
  625. mmioFlush(lpcs->hmmio, 0);
  626. /* Close the file */
  627. mmioClose(lpcs->hmmio, 0);
  628. lpcs->hmmio = NULL;
  629. return fRet;
  630. }
  631. // ****************************************************************
  632. // ******************** Audio Buffer Control **********************
  633. // ****************************************************************
  634. // Audio buffers are always allocated under the presumption that
  635. // audio capture may be enabled at any time.
  636. // AVIAudioInit must be matched with AVIAudioFini (both only called once)
  637. // AVIAudioPrepare must be matched with AVIAudioUnPrepare
  638. // (which may be called multiple times to enable and disable audio)
  639. // AVI AudioInit - Allocate and initialize buffers for audio capture.
  640. // This routine is also used by MCI capture.
  641. // Returns: 0 on success, otherwise an error code.
  642. WORD AVIAudioInit (LPCAPSTREAM lpcs)
  643. {
  644. int i;
  645. LPVOID p;
  646. if (lpcs->sCapParms.wNumAudioRequested == 0)
  647. lpcs->sCapParms.wNumAudioRequested = DEF_WAVE_BUFFERS;
  648. // Alloc the wave memory
  649. for(i = 0; i < (int)lpcs->sCapParms.wNumAudioRequested; i++) {
  650. p = AllocMem(sizeof(WAVEHDR) + lpcs->dwWaveSize, FALSE /* DOSMem */);
  651. if (p == NULL)
  652. break;
  653. lpcs->alpWaveHdr[i] = p;
  654. lpcs->alpWaveHdr[i]->lpData = (LPBYTE)p
  655. + sizeof(WAVEHDR) + sizeof(RIFF);
  656. lpcs->alpWaveHdr[i]->dwBufferLength = lpcs->dwWaveSize - sizeof(RIFF);
  657. lpcs->alpWaveHdr[i]->dwBytesRecorded = 0;
  658. lpcs->alpWaveHdr[i]->dwUser = 0;
  659. lpcs->alpWaveHdr[i]->dwFlags = 0;
  660. lpcs->alpWaveHdr[i]->dwLoops = 0;
  661. /* Set Chunk ID, Size in buffer */
  662. p = (LPBYTE)p + sizeof(WAVEHDR);
  663. ((LPRIFF)p)->dwType = MAKEAVICKID(cktypeWAVEbytes, 1);
  664. ((LPRIFF)p)->dwSize = lpcs->dwWaveSize - sizeof(RIFF);
  665. }
  666. lpcs->iNumAudio = i;
  667. return ((lpcs->iNumAudio == 0) ? IDS_CAP_WAVE_ALLOC_ERROR : 0);
  668. }
  669. //
  670. // AVI AudioFini - UnPrepares headers and resets the wave device.
  671. // This routine is also used by MCI capture.
  672. // Returns: 0 on success, otherwise an error code.
  673. WORD AVIAudioFini (LPCAPSTREAM lpcs)
  674. {
  675. int i;
  676. /* free headers and data */
  677. for(i=0; i < MAX_WAVE_BUFFERS; i++) {
  678. if (lpcs->alpWaveHdr[i]) {
  679. FreeMem(lpcs->alpWaveHdr[i]);
  680. lpcs->alpWaveHdr[i] = NULL;
  681. }
  682. }
  683. return 0;
  684. }
  685. //
  686. // AVI AudioPrepare - Opens the wave device and adds the buffers
  687. // Prepares headers and adds buffers to the device
  688. // This routine is also used by MCI capture.
  689. // Returns: 0 on success, otherwise an error code.
  690. WORD AVIAudioPrepare (LPCAPSTREAM lpcs, HWND hWndCallback)
  691. {
  692. UINT uiError;
  693. int i;
  694. /* See if we can open that format for input */
  695. uiError = waveInOpen((LPHWAVEIN)&lpcs->hWaveIn,
  696. (UINT)WAVE_MAPPER, lpcs->lpWaveFormat,
  697. (DWORD) hWndCallback, 0L,
  698. (hWndCallback ? CALLBACK_WINDOW : 0L));
  699. if (uiError != MMSYSERR_NOERROR)
  700. return IDS_CAP_WAVE_OPEN_ERROR;
  701. lpcs->fAudioYield = IsWaveInDeviceMapped(lpcs->hWaveIn);
  702. lpcs->fAudioBreak = FALSE;
  703. DPF("AVICap: AudioYield = %d \n", lpcs->fAudioYield);
  704. for(i = 0; i < (int)lpcs->sCapParms.wNumAudioRequested; i++) {
  705. if (waveInPrepareHeader(lpcs->hWaveIn, lpcs->alpWaveHdr[i],
  706. sizeof(WAVEHDR)))
  707. return IDS_CAP_WAVE_ALLOC_ERROR;
  708. if (waveInAddBuffer(lpcs->hWaveIn, lpcs->alpWaveHdr[i],
  709. sizeof(WAVEHDR)))
  710. return IDS_CAP_WAVE_ALLOC_ERROR;
  711. }
  712. lpcs->iNextWave = 0; // current wave
  713. lpcs->dwWaveBytes = 0L; // number of wave bytes
  714. lpcs->dwWaveChunkCount = 0; // number of wave frames
  715. return 0;
  716. }
  717. //
  718. // AVI AudioUnPrepare - UnPrepares headers and closes the wave device.
  719. // This routine is also used by MCI capture.
  720. // Returns: 0 on success, otherwise an error code.
  721. WORD AVIAudioUnPrepare (LPCAPSTREAM lpcs)
  722. {
  723. int i;
  724. if (lpcs->hWaveIn) {
  725. waveInReset(lpcs->hWaveIn);
  726. /* unprepare headers by unlocking them */
  727. for(i=0; i < lpcs->iNumAudio; i++) {
  728. if (lpcs->alpWaveHdr[i]) {
  729. if (lpcs->alpWaveHdr[i]->dwFlags & WHDR_PREPARED)
  730. waveInUnprepareHeader(lpcs->hWaveIn, lpcs->alpWaveHdr[i],
  731. sizeof(WAVEHDR));
  732. }
  733. }
  734. waveInClose(lpcs->hWaveIn);
  735. lpcs->hWaveIn = NULL;
  736. }
  737. return 0;
  738. }
  739. // ****************************************************************
  740. // ******************** Video Buffer Control **********************
  741. // ****************************************************************
  742. // AVIVideoInit - Allocates, and initialize buffers for video capture.
  743. // This routine is also used by MCI capture.
  744. // Returns: 0 on success, otherwise an error code.
  745. WORD AVIVideoInit (LPCAPSTREAM lpcs)
  746. {
  747. int iMaxVideo;
  748. DWORD dwFreeMem;
  749. DWORD dwUserRequests;
  750. DWORD dwAudioMem;
  751. int i;
  752. LPVOID p;
  753. lpcs->iNextVideo = 0;
  754. lpcs->dwVideoChunkCount = 0;
  755. lpcs->dwFramesDropped = 0;
  756. // When performing MCI step capture, buffer array is not used
  757. if (lpcs->sCapParms.fStepMCIDevice)
  758. return 0;
  759. // If the user hasn't specified the number of video buffers to use,
  760. // assume the minimum
  761. if (lpcs->sCapParms.wNumVideoRequested == 0)
  762. lpcs->sCapParms.wNumVideoRequested = MIN_VIDEO_BUFFERS;
  763. iMaxVideo = min (MAX_VIDEO_BUFFERS, lpcs->sCapParms.wNumVideoRequested);
  764. // Post VFW 1.1a, see if the driver can allocate memory
  765. if (videoStreamAllocHdrAndBuffer (lpcs->hVideoIn,
  766. (LPVIDEOHDR FAR *) &p, (DWORD) sizeof(VIDEOHDR) + lpcs->dwVideoSize)
  767. == DV_ERR_OK) {
  768. lpcs-> fBuffersOnHardware = TRUE;
  769. videoStreamFreeHdrAndBuffer (lpcs->hVideoIn, (LPVIDEOHDR) p);
  770. }
  771. else {
  772. lpcs-> fBuffersOnHardware = FALSE;
  773. // How much actual free physical memory exists?
  774. dwFreeMem = GetFreePhysicalMemory();
  775. dwAudioMem = lpcs->dwWaveSize * lpcs->sCapParms.wNumAudioRequested;
  776. #define FOREVER_FREE 32768L // Always keep this free for swap space
  777. // How much memory will be used if we allocate per the request?
  778. dwUserRequests = dwAudioMem +
  779. lpcs->dwVideoSize * iMaxVideo +
  780. FOREVER_FREE;
  781. // If request is greater than available memory, force fewer buffers
  782. if (dwUserRequests > dwFreeMem) {
  783. if (dwFreeMem > dwAudioMem)
  784. dwFreeMem -= dwAudioMem;
  785. iMaxVideo = (int)(((dwFreeMem * 8) / 10) / lpcs->dwVideoSize);
  786. iMaxVideo = min (MAX_VIDEO_BUFFERS, iMaxVideo);
  787. dprintf("iMaxVideo = %d\n", iMaxVideo);
  788. }
  789. } // endif not allocating buffers from hardware
  790. // Set up the buffers presuming fixed size DIBs and Junk chunks
  791. // These will be modified later if the device provides compressed data
  792. for (i=0; i < iMaxVideo; i++) {
  793. if (lpcs-> fBuffersOnHardware)
  794. videoStreamAllocHdrAndBuffer (lpcs->hVideoIn,
  795. (LPVIDEOHDR FAR *) &p, sizeof(VIDEOHDR) + lpcs->dwVideoSize);
  796. else
  797. p = AllocMem(sizeof(VIDEOHDR) + lpcs->dwVideoSize, lpcs->sCapParms.fUsingDOSMemory /* DOSMem */);
  798. if (p == NULL)
  799. break;
  800. lpcs->alpVideoHdr[i] = p;
  801. lpcs->alpVideoHdr[i]->lpData = (LPBYTE)p + sizeof(VIDEOHDR) + sizeof(RIFF);
  802. lpcs->alpVideoHdr[i]->dwBufferLength = lpcs->lpBitsInfo->bmiHeader.biSizeImage;
  803. lpcs->alpVideoHdr[i]->dwBytesUsed = 0;
  804. lpcs->alpVideoHdr[i]->dwTimeCaptured = 0;
  805. lpcs->alpVideoHdr[i]->dwUser = 0;
  806. // Buffers on hardware are marked prepared during allocation!
  807. if (!lpcs-> fBuffersOnHardware)
  808. lpcs->alpVideoHdr[i]->dwFlags = 0;
  809. p = (LPBYTE)p + sizeof(VIDEOHDR);
  810. if (lpcs->lpBitsInfo->bmiHeader.biCompression == BI_RLE8)
  811. ((LPRIFF)p)->dwType = MAKEAVICKID(cktypeDIBcompressed, 0);
  812. else
  813. ((LPRIFF)p)->dwType = MAKEAVICKID(cktypeDIBbits, 0);
  814. ((LPRIFF)p)->dwSize = lpcs->lpBitsInfo->bmiHeader.biSizeImage;
  815. if(lpcs->dwVideoJunkSize) {
  816. p = ((BYTE huge *)p) + ((LPRIFF)p)->dwSize + sizeof(RIFF);
  817. ((LPRIFF)p)->dwType = ckidAVIPADDING;;
  818. ((LPRIFF)p)->dwSize = lpcs->dwVideoJunkSize;
  819. }
  820. }
  821. lpcs->iNumVideo = i;
  822. if (lpcs-> fBuffersOnHardware)
  823. dprintf("HARDWARE iNumVideo Allocated = %d \n", lpcs->iNumVideo);
  824. else if (lpcs->sCapParms.fUsingDOSMemory)
  825. dprintf("DOS iNumVideo Allocated = %d \n", lpcs->iNumVideo);
  826. else
  827. dprintf("HIGH iNumVideo Allocated = %d \n", lpcs->iNumVideo);
  828. return ((lpcs->iNumVideo == 0) ? IDS_CAP_VIDEO_ALLOC_ERROR : 0);
  829. }
  830. //
  831. // AVIVideoPrepare - Prepares headers and adds buffers to the device
  832. // This routine is also used by MCI capture.
  833. // Returns: 0 on success, otherwise an error code.
  834. WORD AVIVideoPrepare (LPCAPSTREAM lpcs)
  835. {
  836. int i;
  837. // When performing MCI step capture, buffer array is not used
  838. if (lpcs->sCapParms.fStepMCIDevice)
  839. return 0;
  840. // Open the video stream, setting the capture rate
  841. if (videoStreamInit(lpcs->hVideoIn,
  842. lpcs->sCapParms.dwRequestMicroSecPerFrame,
  843. 0L, 0L, 0L )) {
  844. dprintf("cant open video device!\n");
  845. return IDS_CAP_VIDEO_OPEN_ERROR;
  846. }
  847. // Prepare (lock) the buffers, and give them to the device
  848. for (i=0; i < lpcs->iNumVideo; i++) {
  849. // If the buffers are on the hardware, don't Prepare them
  850. if (!lpcs-> fBuffersOnHardware) {
  851. if (videoStreamPrepareHeader (lpcs->hVideoIn,
  852. lpcs->alpVideoHdr[i], sizeof(VIDEOHDR))) {
  853. lpcs->iNumVideo = i;
  854. dprintf("**** could only prepare %d Video!\n", lpcs->iNumVideo);
  855. break;
  856. }
  857. }
  858. if (videoStreamAddBuffer(lpcs->hVideoIn, lpcs->alpVideoHdr[i], sizeof(VIDEOHDR)))
  859. return IDS_CAP_VIDEO_ALLOC_ERROR;
  860. }
  861. return 0;
  862. }
  863. //
  864. // AVI VideoUnPrepare - UnPrepares headers, frees memory, and
  865. // resets the video in device.
  866. // This routine is also used by MCI capture.
  867. // Returns: 0 on success, otherwise an error code.
  868. WORD AVIVideoUnPrepare (LPCAPSTREAM lpcs)
  869. {
  870. int i;
  871. // When performing MCI step capture, buffer array is not used
  872. if (lpcs->sCapParms.fStepMCIDevice)
  873. return 0;
  874. /* Reset the buffers so they can be freed */
  875. if (lpcs->hVideoIn) {
  876. videoStreamReset(lpcs->hVideoIn);
  877. /* unprepare headers */
  878. /* Unlock and free headers and data */
  879. for(i = 0; i < MAX_VIDEO_BUFFERS; i++) {
  880. if (lpcs->alpVideoHdr[i]) {
  881. if (!lpcs-> fBuffersOnHardware) {
  882. if (lpcs->alpVideoHdr[i]->dwFlags & VHDR_PREPARED)
  883. videoStreamUnprepareHeader(lpcs->hVideoIn,
  884. lpcs->alpVideoHdr[i],sizeof(VIDEOHDR));
  885. FreeMem(lpcs->alpVideoHdr[i]);
  886. }
  887. else
  888. videoStreamFreeHdrAndBuffer(lpcs->hVideoIn, lpcs->alpVideoHdr[i]);
  889. lpcs->alpVideoHdr[i] = NULL;
  890. }
  891. }
  892. // Shut down the video stream
  893. videoStreamFini(lpcs->hVideoIn);
  894. }
  895. return 0;
  896. }
  897. /*
  898. * AVI Fini - undo the mess that AVIInit did.
  899. *
  900. */
  901. void AVIFini(LPCAPSTREAM lpcs)
  902. {
  903. if (lpcs->lpDOSWriteBuffer) {
  904. FreeMem(lpcs->lpDOSWriteBuffer);
  905. lpcs->lpDOSWriteBuffer = NULL;
  906. }
  907. AVIVideoUnPrepare (lpcs); // Free the video device and buffers
  908. AVIAudioUnPrepare (lpcs); // Free the audio device
  909. AVIAudioFini (lpcs); // Free the audio buffers
  910. }
  911. //
  912. // AVI Init
  913. // This routine does all the non-File initalization for AVICapture.
  914. // Returns: 0 on success, Error string value on failure.
  915. //
  916. WORD AVIInit (LPCAPSTREAM lpcs)
  917. {
  918. WORD wError = 0; // Success
  919. int i;
  920. LPBITMAPINFO lpBitsInfoOut; // Possibly compressed output format
  921. /* No special video format given -- use the default */
  922. if (lpcs->CompVars.hic == NULL)
  923. lpBitsInfoOut = lpcs->lpBitsInfo;
  924. else
  925. lpBitsInfoOut = lpcs->CompVars.lpbiOut;
  926. // -------------------------------------------------------
  927. // figure out buffer sizes
  928. // -------------------------------------------------------
  929. // Init all pointers to NULL
  930. for(i = 0; i < MAX_VIDEO_BUFFERS; i++)
  931. lpcs->alpVideoHdr[i] = NULL;
  932. for(i = 0; i < MAX_WAVE_BUFFERS; i++)
  933. lpcs->alpWaveHdr[i] = NULL;
  934. // .5 second of audio per buffer (or 10K, whichever is larger)
  935. if (lpcs->sCapParms.dwAudioBufferSize == 0)
  936. lpcs->dwWaveSize = CalcWaveBufferSize (lpcs);
  937. else {
  938. if (!lpcs-> lpWaveFormat)
  939. lpcs->dwWaveSize = 0;
  940. else
  941. lpcs->dwWaveSize = lpcs->sCapParms.dwAudioBufferSize;
  942. }
  943. /* Set video buffer size to Image size
  944. (normally dx * dy * (depth / 8)) + sizeof(RIFF) */
  945. lpcs->dwVideoSize = lpcs->lpBitsInfo->bmiHeader.biSizeImage + sizeof(RIFF);
  946. lpcs->fVideoDataIsCompressed = (lpBitsInfoOut->bmiHeader.biCompression
  947. != BI_RGB);
  948. /* Pad out to multiple of lpcs->sCapParms.wChunkGranularity (2K) size */
  949. // Calc dwVideoJunkSize
  950. if (lpcs->dwVideoJunkSize = lpcs->sCapParms.wChunkGranularity - (lpcs->dwVideoSize % lpcs->sCapParms.wChunkGranularity)) {
  951. if (lpcs->dwVideoJunkSize < sizeof(RIFF))
  952. lpcs->dwVideoJunkSize += lpcs->sCapParms.wChunkGranularity;
  953. lpcs->dwVideoSize += lpcs->dwVideoJunkSize;
  954. lpcs->dwVideoJunkSize -= sizeof(RIFF);
  955. } else {
  956. lpcs->dwVideoJunkSize = 0L;
  957. }
  958. // -------------------------------------------------------
  959. // DOS copy buffer
  960. // -------------------------------------------------------
  961. lpcs->dwDOSBufferSize = max (lpcs->dwWaveSize, lpcs->dwVideoSize);
  962. #if 0
  963. // Only get a DOS copy buffer if we're not trying to get DOS video buffers
  964. if (!lpcs->sCapParms.fUsingDOSMemory) {
  965. lpcs->lpDOSWriteBuffer = AllocDosMem(lpcs->dwDOSBufferSize);
  966. if (lpcs->lpDOSWriteBuffer) {
  967. dprintf("Allocated DOS write buffer (%ld bytes).\n", lpcs->dwDOSBufferSize);
  968. } else {
  969. dprintf("Unable to allocate DOS write buffer.\n");
  970. }
  971. }
  972. #endif
  973. // -------------------------------------------------------
  974. // Init Sound
  975. // -------------------------------------------------------
  976. if (lpcs->sCapParms.fCaptureAudio) {
  977. if (wError = AVIAudioInit (lpcs)) {
  978. dprintf("can't init audio buffers!\n");
  979. goto AVIInitFailed;
  980. }
  981. }
  982. // -------------------------------------------------------
  983. // Init Video
  984. // -------------------------------------------------------
  985. if (wError = AVIVideoInit (lpcs)) {
  986. dprintf("AVIVideoInitFailed (no buffers alloc'd)!\n");
  987. goto AVIInitFailed;
  988. }
  989. // --------------------------------------------------------------
  990. // Prepare audio buffers (lock em down) and give them to the device
  991. // --------------------------------------------------------------
  992. if (lpcs->sCapParms.fCaptureAudio) {
  993. if (wError = AVIAudioPrepare (lpcs, NULL)) {
  994. dprintf("can't prepare audio buffers!\n");
  995. goto AVIInitFailed;
  996. }
  997. }
  998. // --------------------------------------------------------------
  999. // Prepare video buffers (lock em down) and give them to the device
  1000. // --------------------------------------------------------------
  1001. if (wError = AVIVideoPrepare (lpcs)) {
  1002. dprintf("can't prepare video buffers!\n");
  1003. goto AVIInitFailed;
  1004. }
  1005. // -------------------------------------------------------
  1006. // all done, return success
  1007. // -------------------------------------------------------
  1008. return (0); // SUCCESS !
  1009. // -------------------------------------------------------
  1010. // we got a error, return string ID of error message
  1011. // -------------------------------------------------------
  1012. AVIInitFailed:
  1013. AVIFini(lpcs); // Shutdown everything
  1014. return wError;
  1015. }
  1016. // Write data to the capture file
  1017. // Returns: TRUE on a successful write
  1018. BOOL NEAR PASCAL AVIWrite(LPCAPSTREAM lpcs, LPVOID p, DWORD dwSize)
  1019. {
  1020. if (lpcs->lpDOSWriteBuffer) {
  1021. MemCopy(lpcs->lpDOSWriteBuffer, p, dwSize);
  1022. p = lpcs->lpDOSWriteBuffer;
  1023. }
  1024. return mmioWrite(lpcs->hmmio, p, (long)dwSize) == (long)dwSize;
  1025. }
  1026. //
  1027. // Writes dummy frames which on playback just repeat the previous frame
  1028. // nCount is a count of the number of frames to write
  1029. // Returns: TRUE on a successful write
  1030. BOOL AVIWriteDummyFrames (LPCAPSTREAM lpcs, int nCount)
  1031. {
  1032. DWORD dwBytesToWrite;
  1033. DWORD dwJunkSize;
  1034. LPRIFF p;
  1035. int j;
  1036. p = (LPRIFF) lpcs->DropFrame;
  1037. for (j = 0; j < nCount; j++) {
  1038. // The index includes info on if this is a dummy chunk,
  1039. // AND if this is the last dummy chunk in a sequence
  1040. IndexVideo (lpcs, IS_DUMMY_CHUNK |
  1041. ((j == nCount - 1) ? IS_LAST_DUMMY_CHUNK : 0), FALSE);
  1042. if (lpcs->lpBitsInfo->bmiHeader.biCompression == BI_RLE8)
  1043. p->dwType = MAKEAVICKID(cktypeDIBcompressed, 0);
  1044. else
  1045. p->dwType = MAKEAVICKID(cktypeDIBbits, 0);
  1046. p->dwSize = 0;
  1047. p++;
  1048. }
  1049. dwBytesToWrite = nCount * sizeof(RIFF);
  1050. /* Pad out to multiple of lpcs->sCapParms.wChunkGranularity (2K) size */
  1051. if (dwJunkSize = (dwBytesToWrite % lpcs->sCapParms.wChunkGranularity)) {
  1052. dwJunkSize = lpcs->sCapParms.wChunkGranularity - dwJunkSize;
  1053. if (dwJunkSize < sizeof(RIFF))
  1054. dwJunkSize += lpcs->sCapParms.wChunkGranularity;
  1055. dwBytesToWrite += dwJunkSize;
  1056. dwJunkSize -= sizeof(RIFF);
  1057. } else {
  1058. dwJunkSize = 0L;
  1059. }
  1060. // Now create a new junk chunk at the end of the compressed data
  1061. if(dwJunkSize) {
  1062. p->dwType = ckidAVIPADDING;
  1063. p->dwSize = dwJunkSize;
  1064. }
  1065. /* write out the dummy frames, and possibly the junk chunk */
  1066. return (AVIWrite(lpcs, lpcs->DropFrame, dwBytesToWrite));
  1067. }
  1068. // Writes compressed or uncompressed frames to the AVI file
  1069. // returns TRUE if no error, FALSE if end of file.
  1070. BOOL AVIWriteVideoFrame (LPCAPSTREAM lpcs, LPVIDEOHDR lpVidHdr)
  1071. {
  1072. DWORD dwBytesToWrite;
  1073. DWORD dwJunkSize;
  1074. LPVOID p;
  1075. LPVOID lpData;
  1076. // If the device compresses the data, calculate new junk chunk
  1077. // and fix the RIFF header
  1078. //
  1079. // We are automatically compressing during capture, so
  1080. // first compress the frame.
  1081. //
  1082. if (lpcs->CompVars.hic) {
  1083. DWORD dwBytesUsed = 0; // don't force a data rate
  1084. BOOL fKeyFrame;
  1085. lpData = ICSeqCompressFrame(&lpcs->CompVars, 0,
  1086. lpVidHdr->lpData, &fKeyFrame, &dwBytesUsed);
  1087. ((RIFF FAR*)lpData)[-1].dwType = MAKEAVICKID(cktypeDIBbits, 0);
  1088. ((RIFF FAR*)lpData)[-1].dwSize = dwBytesUsed;
  1089. if (fKeyFrame)
  1090. lpVidHdr->dwFlags |= VHDR_KEYFRAME;
  1091. else
  1092. lpVidHdr->dwFlags &= ~VHDR_KEYFRAME;
  1093. lpVidHdr->dwBytesUsed = dwBytesUsed;
  1094. }
  1095. else {
  1096. lpData = lpVidHdr->lpData;
  1097. }
  1098. if (lpcs->fVideoDataIsCompressed) { // ie. if not BI_RGB
  1099. // change the dwSize field in the RIFF chunk
  1100. *((LPDWORD)((BYTE _huge *)lpVidHdr->lpData - sizeof(DWORD)))
  1101. = lpVidHdr->dwBytesUsed;
  1102. // Make sure that the JUNK chunk starts on a WORD boundary
  1103. if (lpVidHdr->dwBytesUsed & 1)
  1104. ++lpVidHdr->dwBytesUsed;
  1105. dwBytesToWrite = lpVidHdr->dwBytesUsed + sizeof(RIFF);
  1106. /* Pad out to multiple of lpcs->sCapParms.wChunkGranularity (2K) size */
  1107. if (dwJunkSize = (dwBytesToWrite % lpcs->sCapParms.wChunkGranularity)) {
  1108. dwJunkSize = lpcs->sCapParms.wChunkGranularity - dwJunkSize;
  1109. if (dwJunkSize < sizeof(RIFF))
  1110. dwJunkSize += lpcs->sCapParms.wChunkGranularity;
  1111. dwBytesToWrite += dwJunkSize;
  1112. // Now create a new junk chunk at the end of the compressed data
  1113. p = (BYTE huge *)lpVidHdr->lpData + lpVidHdr->dwBytesUsed;
  1114. ((LPRIFF)p)->dwType = ckidAVIPADDING;
  1115. ((LPRIFF)p)->dwSize = dwJunkSize - sizeof(RIFF);
  1116. }
  1117. } // endif compressed data
  1118. else {
  1119. dwBytesToWrite = lpcs->dwVideoSize;
  1120. } // endif not compressed data
  1121. /* write out the chunk, video data, and possibly the junk chunk */
  1122. return (AVIWrite(lpcs, (LPBYTE)lpData - sizeof(RIFF), dwBytesToWrite));
  1123. }
  1124. //
  1125. // Maintains info chunks which are written to the AVI header
  1126. //
  1127. BOOL FAR PASCAL SetInfoChunk(LPCAPSTREAM lpcs, LPCAPINFOCHUNK lpcic)
  1128. {
  1129. DWORD ckid = lpcic->fccInfoID;
  1130. LPVOID lpData = lpcic->lpData;
  1131. LONG cbData = lpcic->cbData;
  1132. LPBYTE lp;
  1133. LPBYTE lpw;
  1134. LPBYTE lpEnd;
  1135. LPBYTE lpNext;
  1136. LONG cbSizeThis;
  1137. BOOL fOK = FALSE;
  1138. // Delete all info chunks?
  1139. if (ckid == 0) {
  1140. if (lpcs->lpInfoChunks) {
  1141. GlobalFreePtr (lpcs->lpInfoChunks);
  1142. lpcs->lpInfoChunks = NULL;
  1143. lpcs->cbInfoChunks = 0;
  1144. }
  1145. return TRUE;
  1146. }
  1147. // Try removing an entry if it already exists...
  1148. // Also used if lpData is NULL to just remove an entry
  1149. lpw = (LPBYTE)lpcs->lpInfoChunks; // always points at fcc
  1150. lpEnd = (LPBYTE)lpcs->lpInfoChunks + lpcs->cbInfoChunks;
  1151. while (lpw < lpEnd) {
  1152. cbSizeThis = ((LPDWORD)lpw)[1];
  1153. cbSizeThis += cbSizeThis & 1; // force WORD alignment
  1154. lpNext = lpw + cbSizeThis + sizeof (DWORD) * 2;
  1155. if ((*(LPDWORD) lpw) == ckid) {
  1156. lpcs->cbInfoChunks -= cbSizeThis + sizeof (DWORD) * 2;
  1157. if (lpNext <= lpEnd) {
  1158. if (lpEnd - lpNext)
  1159. hmemcpy(lpw, lpNext, lpEnd - lpNext);
  1160. if (lpcs->cbInfoChunks) {
  1161. lpcs->lpInfoChunks = (LPBYTE) GlobalReAllocPtr( // shrink it
  1162. lpcs->lpInfoChunks,
  1163. lpcs->cbInfoChunks,
  1164. GMEM_MOVEABLE);
  1165. }
  1166. else {
  1167. if (lpcs->lpInfoChunks)
  1168. GlobalFreePtr (lpcs->lpInfoChunks);
  1169. lpcs->lpInfoChunks = NULL;
  1170. }
  1171. fOK = TRUE;
  1172. }
  1173. break;
  1174. }
  1175. else
  1176. lpw = lpNext;
  1177. }
  1178. if (lpData == NULL || cbData == 0) // Only deleting, get out
  1179. return fOK;
  1180. // Add a new entry
  1181. cbData += cbData & 1; // force WORD alignment
  1182. cbData += sizeof(DWORD) * 2; // add sizeof 2 FOURCCs
  1183. if (lpcs->lpInfoChunks) {
  1184. lp = (LPBYTE) GlobalReAllocPtr(lpcs->lpInfoChunks, lpcs->cbInfoChunks + cbData, GMEM_MOVEABLE);
  1185. } else {
  1186. lp = (LPBYTE) GlobalAllocPtr(GMEM_MOVEABLE, cbData);
  1187. }
  1188. if (!lp)
  1189. return FALSE;
  1190. // build RIFF chunk in block
  1191. ((DWORD FAR *) (lp + lpcs->cbInfoChunks))[0] = ckid;
  1192. ((DWORD FAR *) (lp + lpcs->cbInfoChunks))[1] = lpcic->cbData;
  1193. hmemcpy(lp + lpcs->cbInfoChunks + sizeof(DWORD) * 2,
  1194. lpData,
  1195. cbData - sizeof(DWORD) * 2);
  1196. lpcs->lpInfoChunks = lp;
  1197. lpcs->cbInfoChunks += cbData;
  1198. return TRUE;
  1199. }
  1200. /*
  1201. * AVI Capture
  1202. * This is the main streaming capture loop for both audio and
  1203. * video. It will first init all buffers and drivers and then go into a
  1204. * loop checking for buffers to be filled. When a buffer is filled then
  1205. * the data for it is written out.
  1206. * Afterwards it cleans up after itself (frees buffers etc...)
  1207. * Returns: 0 on success, else error code
  1208. */
  1209. void FAR PASCAL _loadds AVICapture1(LPCAPSTREAM lpcs)
  1210. {
  1211. BOOL fOK = TRUE;
  1212. BOOL fT;
  1213. BOOL fVideoBuffersInDOSMem;
  1214. BOOL fStopping; // True when finishing capture
  1215. BOOL fStopped; // True if driver notified to stop
  1216. DWORD dw;
  1217. char ach[128];
  1218. char achMsg[128];
  1219. WORD w;
  1220. WORD wError; // Error String ID
  1221. DWORD dwDriverDropCount;
  1222. WORD wSmartDrv;
  1223. LPVIDEOHDR lpVidHdr;
  1224. LPWAVEHDR lpWaveHdr;
  1225. DWORD dwTimeStarted; // When did we start in milliseconds
  1226. DWORD dwTimeStopped;
  1227. DWORD dwTimeToStop; // Lesser of MCI capture time or frame limit
  1228. BOOL fTryToPaint = FALSE;
  1229. HDC hdc;
  1230. HPALETTE hpalT;
  1231. HCURSOR hOldCursor;
  1232. RECT rcDrawRect;
  1233. DWORD dwStreamError;
  1234. CAPINFOCHUNK cic;
  1235. lpcs-> dwReturn = DV_ERR_OK;
  1236. hOldCursor = SetCursor(lpcs->hWaitCursor);
  1237. statusUpdateStatus(lpcs, IDS_CAP_BEGIN); // Always the first message
  1238. // If not 1 Meg. free, give it up!!!
  1239. if (GetFreePhysicalMemory () < (1024L * 1024L)) {
  1240. errorUpdateError (lpcs, IDS_CAP_OUTOFMEM);
  1241. lpcs-> dwReturn = IDS_CAP_OUTOFMEM;
  1242. goto EarlyExit;
  1243. }
  1244. statusUpdateStatus(lpcs, IDS_CAP_STAT_CAP_INIT);
  1245. // Try painting the DIB only if Live window
  1246. fTryToPaint = lpcs->fLiveWindow;
  1247. if (fTryToPaint) {
  1248. hdc = GetDC(lpcs->hwnd);
  1249. SetWindowOrg(hdc, lpcs->ptScroll.x, lpcs->ptScroll.y);
  1250. hpalT = DrawDibGetPalette (lpcs->hdd);
  1251. if (hpalT)
  1252. hpalT = SelectPalette( hdc, hpalT, FALSE);
  1253. RealizePalette(hdc);
  1254. if (lpcs-> fScale)
  1255. GetClientRect (lpcs->hwnd, &rcDrawRect);
  1256. else
  1257. SetRect (&rcDrawRect, 0, 0, lpcs->dxBits, lpcs->dyBits);
  1258. }
  1259. // -------------------------------------------------------
  1260. // When should capture stop?
  1261. // -------------------------------------------------------
  1262. // If using MCI, capture for the shorter of the MCI period,
  1263. // or the capture limit
  1264. if (lpcs->sCapParms.fLimitEnabled)
  1265. dwTimeToStop = (DWORD) ((DWORD) 1000 * lpcs->sCapParms.wTimeLimit);
  1266. else
  1267. dwTimeToStop = (DWORD) -1L; // very large
  1268. if (lpcs->sCapParms.fMCIControl) {
  1269. // if MCI stop time not given, use lpcs->sCapParms.wTimeLimit
  1270. if (lpcs->sCapParms.dwMCIStopTime == lpcs->sCapParms.dwMCIStartTime)
  1271. lpcs->sCapParms.dwMCIStopTime = lpcs->sCapParms.dwMCIStartTime +
  1272. (DWORD) ((DWORD)1000 * lpcs->sCapParms.wTimeLimit);
  1273. dw = lpcs->sCapParms.dwMCIStopTime - lpcs->sCapParms.dwMCIStartTime;
  1274. if (lpcs->sCapParms.fLimitEnabled)
  1275. dwTimeToStop = min (dw, dwTimeToStop);
  1276. else
  1277. dwTimeToStop = dw;
  1278. }
  1279. //
  1280. // never ever try to capture more than the index size!
  1281. //
  1282. if (lpcs->fCapturingToDisk) {
  1283. dw = muldiv32(lpcs->sCapParms.dwIndexSize,
  1284. lpcs->sCapParms.dwRequestMicroSecPerFrame,
  1285. 1000l);
  1286. dwTimeToStop = min (dw, dwTimeToStop);
  1287. }
  1288. if (lpcs->sCapParms.fMCIControl) {
  1289. fOK = FALSE; // Assume the worst
  1290. if (MCIDeviceOpen (lpcs)) {
  1291. if (MCIDeviceSetPosition (lpcs, lpcs->sCapParms.dwMCIStartTime))
  1292. fOK = TRUE;
  1293. }
  1294. if (!fOK) {
  1295. errorUpdateError (lpcs, IDS_CAP_MCI_CONTROL_ERROR);
  1296. statusUpdateStatus(lpcs, NULL); // Clear status
  1297. lpcs-> dwReturn = IDS_CAP_MCI_CONTROL_ERROR;
  1298. goto EarlyExit;
  1299. }
  1300. }
  1301. //
  1302. // If we're compressing while capturing, warm up the compressor
  1303. //
  1304. if (lpcs->CompVars.hic) {
  1305. if (ICSeqCompressFrameStart(&lpcs->CompVars, lpcs->lpBitsInfo) == NULL) {
  1306. // !!! We're in trouble here!
  1307. dprintf("ICSeqCompressFrameStart failed !!!\n");
  1308. lpcs-> dwReturn = IDS_CAP_COMPRESSOR_ERROR;
  1309. errorUpdateError (lpcs, IDS_CAP_COMPRESSOR_ERROR);
  1310. goto EarlyExit;
  1311. }
  1312. // Kludge, offset the lpBitsOut ptr
  1313. // Compman allocates the compress buffer too large by
  1314. // 2048 + 16 so we will still have room
  1315. ((LPBYTE) lpcs->CompVars.lpBitsOut) += 8;
  1316. }
  1317. // No compression desired
  1318. if (!lpcs->CompVars.hic)
  1319. WinAssert(lpcs->CompVars.lpbiOut == NULL);
  1320. // -------------------------------------------------------
  1321. // Open the output file
  1322. // -------------------------------------------------------
  1323. if (lpcs->fCapturingToDisk) {
  1324. if (!AVIFileInit(lpcs)) {
  1325. lpcs-> dwReturn = IDS_CAP_FILE_OPEN_ERROR;
  1326. errorUpdateError (lpcs, IDS_CAP_FILE_OPEN_ERROR);
  1327. goto EarlyExit;
  1328. }
  1329. }
  1330. /* Make sure the parent has been repainted */
  1331. UpdateWindow(lpcs->hwnd);
  1332. //
  1333. // call AVIInit() to get all the capture memory we will need
  1334. //
  1335. // Don't use DOS memory if capturing to Net
  1336. fVideoBuffersInDOSMem = lpcs->sCapParms.fUsingDOSMemory;
  1337. wError = AVIInit(lpcs);
  1338. if (wError && fVideoBuffersInDOSMem) {
  1339. lpcs->sCapParms.fUsingDOSMemory = FALSE;
  1340. wError = AVIInit(lpcs);
  1341. }
  1342. if (wError) {
  1343. /* Error in initalization - return */
  1344. errorUpdateError (lpcs, wError);
  1345. AVIFini(lpcs);
  1346. AVIFileFini(lpcs, TRUE /* fWroteJunkChunks */, TRUE /* fAbort */);
  1347. statusUpdateStatus(lpcs, NULL); // Clear status
  1348. lpcs-> dwReturn = wError;
  1349. goto EarlyExit;
  1350. }
  1351. /* Click OK to capture string (must follow AVIInit) */
  1352. LoadString(lpcs->hInst, IDS_CAP_SEQ_MSGSTART, ach, sizeof(ach));
  1353. wsprintf(achMsg, ach, (LPSTR)lpcs->achFile);
  1354. statusUpdateStatus(lpcs, NULL);
  1355. // -------------------------------------------------------
  1356. // Ready to go, make the user click OK?
  1357. // -------------------------------------------------------
  1358. if (lpcs->sCapParms.fMakeUserHitOKToCapture && lpcs->fCapturingToDisk) {
  1359. w = MessageBox(lpcs->hwnd, achMsg, "", MB_OKCANCEL | MB_ICONEXCLAMATION);
  1360. if (w == IDCANCEL) {
  1361. /* clean-up and get out */
  1362. AVIFini(lpcs);
  1363. AVIFileFini(lpcs, TRUE /* fWroteJunkChunks */, TRUE /* fAbort */);
  1364. statusUpdateStatus(lpcs, NULL); // Clear status
  1365. goto EarlyExit;
  1366. }
  1367. } // endif forcing user to hit OK
  1368. /* update the status, so the user knows how to stop */
  1369. statusUpdateStatus(lpcs, IDS_CAP_SEQ_MSGSTOP);
  1370. UpdateWindow(lpcs->hwnd);
  1371. lpcs-> fCapturingNow = TRUE;
  1372. GetAsyncKeyState(lpcs->sCapParms.vKeyAbort);
  1373. GetAsyncKeyState(VK_ESCAPE);
  1374. GetAsyncKeyState(VK_LBUTTON);
  1375. GetAsyncKeyState(VK_RBUTTON);
  1376. if (lpcs->sCapParms.fDisableWriteCache)
  1377. wSmartDrv = SmartDrv(lpcs->achFile[0], (WORD)-1); // turn all off....
  1378. // Insert the digitization time
  1379. cic.fccInfoID = mmioFOURCC ('I','D','I','T');
  1380. time (&ltime);
  1381. cic.lpData = (LPSTR) ctime(&ltime);
  1382. cic.cbData = 26;
  1383. SetInfoChunk (lpcs, &cic);
  1384. // -------------------------------------------------------
  1385. // Start MCI, Audio, and video streams
  1386. // -------------------------------------------------------
  1387. if (lpcs-> CallbackOnControl) {
  1388. // Callback will preroll, then return on frame accurate postion
  1389. // The 1 indicates recording is about to start
  1390. // Callback can return FALSE to exit without capturing
  1391. if (!((*(lpcs->CallbackOnControl)) (lpcs->hwnd, CONTROLCALLBACK_PREROLL ))) {
  1392. /* clean-up and get out */
  1393. AVIFini(lpcs);
  1394. AVIFileFini(lpcs, TRUE /* fWroteJunkChunks */, TRUE /* fAbort */);
  1395. statusUpdateStatus(lpcs, NULL); // Clear status
  1396. goto EarlyExit;
  1397. }
  1398. }
  1399. if (lpcs->sCapParms.fMCIControl)
  1400. MCIDevicePlay (lpcs);
  1401. dwTimeStarted = timeGetTime();
  1402. if(lpcs->sCapParms.fCaptureAudio)
  1403. waveInStart(lpcs->hWaveIn);
  1404. videoStreamStart(lpcs->hVideoIn);
  1405. // -------------------------------------------------------
  1406. // MAIN CAPTURE LOOP
  1407. // -------------------------------------------------------
  1408. fOK=TRUE;
  1409. fStopping = FALSE; // TRUE when we need to stop
  1410. fStopped = FALSE; // TRUE if drivers notified we have stopped
  1411. lpcs->dwTimeElapsedMS = 0;
  1412. lpVidHdr = lpcs->alpVideoHdr[lpcs->iNextVideo];
  1413. lpWaveHdr = lpcs->alpWaveHdr[lpcs->iNextWave];
  1414. for (;;) {
  1415. // The INTEL driver uses the GetError message to
  1416. // process buffers, so call it often...
  1417. videoStreamGetError (lpcs->hVideoIn, &dwStreamError, &dwDriverDropCount);
  1418. // What time is it?
  1419. lpcs->dwTimeElapsedMS = timeGetTime() - dwTimeStarted;
  1420. // -------------------------------------------------------
  1421. // Is video buffer ready to be written?
  1422. // -------------------------------------------------------
  1423. if ((lpVidHdr->dwFlags & VHDR_DONE)) {
  1424. if (lpVidHdr-> dwBytesUsed) {
  1425. // Current time in milliseconds
  1426. dw = muldiv32 ((lpcs->dwVideoChunkCount + 1),
  1427. lpcs->sCapParms.dwRequestMicroSecPerFrame, 1000);
  1428. if (lpcs->CallbackOnVideoStream)
  1429. (*(lpcs->CallbackOnVideoStream)) (lpcs->hwnd, lpVidHdr);
  1430. if (lpcs-> fCapturingToDisk) {
  1431. if (lpcs->dwVideoChunkCount &&
  1432. (dw < lpVidHdr->dwTimeCaptured)) {
  1433. // Has the capture device skipped frames?
  1434. // w = # of frames skipped
  1435. w = (WORD) muldiv32 ((lpVidHdr-> dwTimeCaptured - dw),
  1436. 1000,
  1437. lpcs->sCapParms.dwRequestMicroSecPerFrame);
  1438. w = min (w, (sizeof (lpcs->DropFrame) / sizeof (RIFF) - sizeof (RIFF) ) );
  1439. lpcs->dwFramesDropped+= w;
  1440. fOK = AVIWriteDummyFrames (lpcs, w);
  1441. if (!fOK)
  1442. fStopping = TRUE;
  1443. } // end if writing dummy frames
  1444. if (!AVIWriteVideoFrame (lpcs, lpVidHdr)) {
  1445. fOK = FALSE;
  1446. fStopping = TRUE;
  1447. // "ERROR: Could not write to file."
  1448. errorUpdateError(lpcs, IDS_CAP_FILE_WRITE_ERROR);
  1449. }
  1450. else {
  1451. if (!IndexVideo(lpcs, lpVidHdr-> dwBytesUsed,
  1452. (BOOL) (lpVidHdr->dwFlags & VHDR_KEYFRAME)))
  1453. fStopping = TRUE;
  1454. }
  1455. } // endif fCapturingToDisk
  1456. // Warning: Kludge to create frame chunk count when net capture
  1457. // follows.
  1458. else
  1459. lpcs->dwVideoChunkCount++;
  1460. // -------------------------------------------------------
  1461. // if we have *nothing* to do paint or show status.
  1462. // -------------------------------------------------------
  1463. w = (lpcs->iNextVideo + 1) % lpcs->iNumVideo;
  1464. if (!(lpcs->alpVideoHdr[w]-> dwFlags & VHDR_DONE)) {
  1465. if (fTryToPaint && lpcs->dwVideoChunkCount &&
  1466. lpVidHdr-> dwFlags & VHDR_KEYFRAME) {
  1467. fTryToPaint = DrawDibDraw(lpcs->hdd, hdc,
  1468. 0, 0,
  1469. rcDrawRect.right - rcDrawRect.left,
  1470. rcDrawRect.bottom - rcDrawRect.top,
  1471. /*lpcs->dxBits, lpcs->dyBits, */
  1472. (LPBITMAPINFOHEADER)lpcs->lpBitsInfo,
  1473. lpVidHdr-> lpData, 0, 0, -1, -1,
  1474. DDF_SAME_HDC | DDF_SAME_DIB | DDF_SAME_SIZE);
  1475. }
  1476. }
  1477. // if there is still more time, (or at least every 100 frames)
  1478. // show status if we're not ending the capture
  1479. if ((!fStopping) && (lpcs-> fCapturingToDisk) &&
  1480. ((lpcs->dwVideoChunkCount && (lpcs->dwVideoChunkCount % 100 == 0)) ||
  1481. (!(lpcs->alpVideoHdr[w]-> dwFlags & VHDR_DONE)) ) ) {
  1482. // "Captured %ld frames (Dropped %ld) %d.%03d sec. Hit Escape to Stop"
  1483. statusUpdateStatus(lpcs, IDS_CAP_STAT_VIDEOCURRENT,
  1484. lpcs->dwVideoChunkCount, lpcs->dwFramesDropped,
  1485. (int)(lpcs-> dwTimeElapsedMS/1000), (int)(lpcs-> dwTimeElapsedMS%1000)
  1486. );
  1487. } // endif next buffer not ready
  1488. } // endif any bytes used in the buffer
  1489. /* return the emptied buffer to the que */
  1490. lpVidHdr->dwFlags &= ~VHDR_DONE;
  1491. if (videoStreamAddBuffer(lpcs->hVideoIn,
  1492. lpVidHdr, sizeof (VIDEOHDR))) {
  1493. fOK = FALSE;
  1494. fStopping = TRUE;
  1495. // "ERROR: Could not re-add buffer."
  1496. errorUpdateError (lpcs, IDS_CAP_VIDEO_ADD_ERROR);
  1497. }
  1498. /* increment the next Video buffer pointer */
  1499. if (++lpcs->iNextVideo >= lpcs->iNumVideo)
  1500. lpcs->iNextVideo = 0;
  1501. lpVidHdr = lpcs->alpVideoHdr[lpcs->iNextVideo];
  1502. }
  1503. if (lpcs-> CallbackOnYield) {
  1504. // If the yield callback returns FALSE, abort
  1505. if (!((*(lpcs->CallbackOnYield)) (lpcs->hwnd)))
  1506. fStopping = TRUE;
  1507. }
  1508. // Don't do peekMessage yield for ACM
  1509. if (lpcs->sCapParms.fYield) {
  1510. MSG msg;
  1511. if (PeekMessage(&msg,NULL,0,0,PM_REMOVE)) {
  1512. // Kludge to get rid of timers from lpcs->hwnd
  1513. if (msg.message == WM_TIMER && msg.hwnd == lpcs->hwnd)
  1514. ;
  1515. else {
  1516. TranslateMessage(&msg);
  1517. DispatchMessage(&msg);
  1518. }
  1519. }
  1520. }
  1521. if (lpcs-> CallbackOnControl) {
  1522. // Outside routine is handling when to stop
  1523. // The CONTROLCALLBACK_CAPTURING indicates we're asking when to stop
  1524. if (!((*(lpcs->CallbackOnControl)) (lpcs->hwnd, CONTROLCALLBACK_CAPTURING )))
  1525. fStopping = TRUE;
  1526. }
  1527. // -------------------------------------------------------
  1528. // Is audio buffer ready to be written?
  1529. // -------------------------------------------------------
  1530. if (lpcs->sCapParms.fCaptureAudio) {
  1531. int iLastWave;
  1532. //
  1533. // we may need to yield for audio to get converted.
  1534. //
  1535. if (lpcs->fAudioYield)
  1536. Yield();
  1537. //
  1538. // if all buffers are done, we have broke audio.
  1539. //
  1540. iLastWave = lpcs->iNextWave == 0 ?
  1541. lpcs->iNumAudio -1 : lpcs->iNextWave-1;
  1542. if (!fStopping &&
  1543. lpcs->alpWaveHdr[iLastWave]->dwFlags & WHDR_DONE)
  1544. lpcs->fAudioBreak = TRUE;
  1545. w = lpcs->iNumAudio; // don't get stuck here forever...
  1546. while (w && fOK && (lpWaveHdr-> dwFlags & WHDR_DONE)) {
  1547. w--;
  1548. if (lpWaveHdr-> dwBytesRecorded) {
  1549. /* Chunk info is included in the wave data */
  1550. /* Reset Chunk Size in buffer */
  1551. ((LPRIFF)(lpWaveHdr->lpData))[-1].dwSize =
  1552. lpWaveHdr-> dwBytesRecorded;
  1553. if (lpcs-> CallbackOnWaveStream) {
  1554. (*(lpcs->CallbackOnWaveStream)) (lpcs->hwnd, lpWaveHdr);
  1555. }
  1556. if (lpcs-> fCapturingToDisk) {
  1557. if(!AVIWrite (lpcs, lpWaveHdr-> lpData - sizeof(RIFF),
  1558. (lpWaveHdr-> dwBytesRecorded +
  1559. sizeof (RIFF) + 1) & ~1L)) {
  1560. fOK = FALSE;
  1561. fStopping = TRUE;
  1562. errorUpdateError (lpcs, IDS_CAP_FILE_WRITE_ERROR);
  1563. } else {
  1564. if (IndexAudio (lpcs, lpWaveHdr-> dwBytesRecorded))
  1565. lpcs->dwWaveBytes += lpWaveHdr-> dwBytesRecorded;
  1566. else
  1567. fStopping = TRUE;
  1568. }
  1569. } // endif capturing to disk
  1570. // Warning: Kludge to create wave chunk count when net capture
  1571. // follows.
  1572. else {
  1573. lpcs->dwWaveChunkCount++;
  1574. lpcs->dwWaveBytes += lpWaveHdr-> dwBytesRecorded;
  1575. }
  1576. } // endif dwBytesRecorded
  1577. lpWaveHdr-> dwBytesRecorded = 0;
  1578. lpWaveHdr-> dwFlags &= ~WHDR_DONE;
  1579. /* return the emptied buffer to the que */
  1580. if(waveInAddBuffer(lpcs->hWaveIn, lpWaveHdr, sizeof(WAVEHDR))) {
  1581. fOK = FALSE;
  1582. fStopping = TRUE;
  1583. errorUpdateError(lpcs, IDS_CAP_WAVE_ADD_ERROR);
  1584. }
  1585. /* increment the next wave buffer pointer */
  1586. if(++lpcs->iNextWave >= lpcs->iNumAudio)
  1587. lpcs->iNextWave = 0;
  1588. lpWaveHdr = lpcs->alpWaveHdr[lpcs->iNextWave];
  1589. } // endwhile buffer available
  1590. } // endif sound enabled
  1591. // -------------------------------------------------------
  1592. // is there any reason to stop?
  1593. // -------------------------------------------------------
  1594. if (lpcs->sCapParms.vKeyAbort) {
  1595. if (GetAsyncKeyState(lpcs->sCapParms.vKeyAbort & 0x00ff) & 0x0001) {
  1596. fT = TRUE;
  1597. if (lpcs->sCapParms.vKeyAbort & 0x8000) // Ctrl?
  1598. fT = fT && (GetAsyncKeyState(VK_CONTROL) & 0x8000);
  1599. if (lpcs->sCapParms.vKeyAbort & 0x4000) // Shift?
  1600. fT = fT && (GetAsyncKeyState(VK_SHIFT) & 0x8000);
  1601. fStopping = fT; // User aborts
  1602. }
  1603. }
  1604. if (lpcs->sCapParms.fAbortLeftMouse)
  1605. if (GetAsyncKeyState(VK_LBUTTON) & 0x0001)
  1606. fStopping = TRUE; // User aborts
  1607. if (lpcs->sCapParms.fAbortRightMouse)
  1608. if (GetAsyncKeyState(VK_RBUTTON) & 0x0001)
  1609. fStopping = TRUE; // User aborts
  1610. if (lpcs-> fAbortCapture || lpcs-> fStopCapture)
  1611. fStopping = TRUE; // Somebody above wants us to quit
  1612. if (lpcs-> dwTimeElapsedMS > dwTimeToStop)
  1613. fStopping = TRUE; // all done
  1614. // -------------------------------------------------------
  1615. // Quit only when we have stopped, and
  1616. // no more buffers are pending from any device.
  1617. // -------------------------------------------------------
  1618. if (fStopped) {
  1619. if (!(lpVidHdr-> dwFlags & VHDR_DONE)) {
  1620. if (lpcs->sCapParms.fCaptureAudio) {
  1621. if (!(lpWaveHdr-> dwFlags & WHDR_DONE))
  1622. break;
  1623. }
  1624. else
  1625. break;
  1626. }
  1627. }
  1628. // -------------------------------------------------------
  1629. // Tell all the devices to stop
  1630. // -------------------------------------------------------
  1631. if (fStopping && !fStopped) {
  1632. fStopped = TRUE;
  1633. DSTATUS(lpcs, "Stopping....");
  1634. if(lpcs->sCapParms.fCaptureAudio) {
  1635. DSTATUS(lpcs, "Stopping Audio");
  1636. waveInStop(lpcs->hWaveIn);
  1637. }
  1638. DSTATUS(lpcs, "Stopping Video");
  1639. videoStreamStop(lpcs->hVideoIn); // Stop everybody
  1640. dwTimeStopped = timeGetTime ();
  1641. if (lpcs->sCapParms.fMCIControl) {
  1642. DSTATUS(lpcs, "Stopping MCI");
  1643. MCIDevicePause (lpcs);
  1644. }
  1645. DSTATUS(lpcs, "Stopped");
  1646. SetCursor(lpcs->hWaitCursor); // Force cursor back to hourglass
  1647. }
  1648. if (fStopping) {
  1649. // "Finished capture, now writing frame %ld"
  1650. if (fOK) {
  1651. statusUpdateStatus(lpcs, IDS_CAP_STAT_CAP_FINI, lpcs->dwVideoChunkCount);
  1652. }
  1653. else { // Exit if problems
  1654. statusUpdateStatus(lpcs, IDS_CAP_RECORDING_ERROR2);
  1655. break;
  1656. }
  1657. }
  1658. } // end of forever
  1659. // -------------------------------------------------------
  1660. // END OF MAIN CAPTURE LOOP
  1661. // -------------------------------------------------------
  1662. if (lpcs->sCapParms.fDisableWriteCache)
  1663. SmartDrv(lpcs->achFile[0], wSmartDrv); // turn Smartdrive back on
  1664. /* eat any keys that have been pressed */
  1665. while(GetKey(FALSE))
  1666. ;
  1667. AVIFini(lpcs); // does the Reset, and frees all buffers
  1668. AVIFileFini(lpcs, TRUE /* fWroteJunkChunks */, FALSE /* fAbort */);
  1669. // This is the corrected capture duration, based on audio samples
  1670. lpcs->dwTimeElapsedMS = lpcs->dwActualMicroSecPerFrame *
  1671. lpcs->dwVideoChunkCount / 1000;
  1672. /* Notify if there was an error while recording */
  1673. if(!fOK) {
  1674. errorUpdateError (lpcs, IDS_CAP_RECORDING_ERROR);
  1675. }
  1676. if (lpcs-> fCapturingToDisk) {
  1677. if (lpcs->dwVideoChunkCount)
  1678. dw = muldiv32(lpcs->dwVideoChunkCount,1000000,lpcs-> dwTimeElapsedMS);
  1679. else
  1680. dw = 0; // The muldiv32 doesn't give 0 if numerator is zero
  1681. if(lpcs->sCapParms.fCaptureAudio) {
  1682. // "Captured %d.%03d sec. %ld frames (%ld dropped) (%d.%03d fps). %ld audio bytes (%d.%03d sps)"
  1683. statusUpdateStatus(lpcs, IDS_CAP_STAT_VIDEOAUDIO,
  1684. (WORD)(lpcs-> dwTimeElapsedMS/1000),
  1685. (WORD)(lpcs-> dwTimeElapsedMS%1000),
  1686. lpcs->dwVideoChunkCount,
  1687. lpcs->dwFramesDropped,
  1688. (WORD)(dw / 1000),
  1689. (WORD)(dw % 1000),
  1690. lpcs->dwWaveBytes,
  1691. (WORD) lpcs->lpWaveFormat->nSamplesPerSec / 1000,
  1692. (WORD) lpcs->lpWaveFormat->nSamplesPerSec % 1000);
  1693. } else {
  1694. // "Captured %d.%03d sec. %ld frames (%ld dropped) (%d.%03d fps)."
  1695. statusUpdateStatus(lpcs, IDS_CAP_STAT_VIDEOONLY,
  1696. (WORD)(lpcs-> dwTimeElapsedMS/1000),
  1697. (WORD)(lpcs-> dwTimeElapsedMS%1000),
  1698. lpcs->dwVideoChunkCount,
  1699. lpcs->dwFramesDropped,
  1700. (WORD)(dw / 1000),
  1701. (WORD)(dw % 1000));
  1702. }
  1703. } // endif capturing to disk (no warnings or errors if to net)
  1704. // No frames captured, warn user that interrupts are probably not enabled.
  1705. if (fOK && (lpcs->dwVideoChunkCount == 0)) {
  1706. errorUpdateError (lpcs, IDS_CAP_NO_FRAME_CAP_ERROR);
  1707. }
  1708. // No audio captured, (but enabled), warn user audio card is hosed
  1709. else if (fOK && lpcs->sCapParms.fCaptureAudio && (lpcs->dwWaveBytes == 0)) {
  1710. errorUpdateError (lpcs, IDS_CAP_NO_AUDIO_CAP_ERROR);
  1711. }
  1712. // Audio underrun, inform user
  1713. else if (fOK && lpcs->sCapParms.fCaptureAudio && lpcs->fAudioBreak) {
  1714. errorUpdateError (lpcs, IDS_CAP_AUDIO_DROP_ERROR);
  1715. }
  1716. // If frames dropped, or changed capture rate, warn the user
  1717. else if (fOK && lpcs->dwVideoChunkCount && lpcs->fCapturingToDisk) {
  1718. // Warn user if dropped > 10% (default) of the frames
  1719. if ((DWORD)100 * lpcs->dwFramesDropped / lpcs->dwVideoChunkCount >
  1720. lpcs-> sCapParms.wPercentDropForError) {
  1721. // "%ld of %ld frames (%d.%03d\%) dropped during capture."
  1722. errorUpdateError (lpcs, IDS_CAP_STAT_FRAMESDROPPED,
  1723. lpcs->dwFramesDropped,
  1724. lpcs->dwVideoChunkCount,
  1725. (WORD)(muldiv32(lpcs->dwFramesDropped,10000,lpcs->dwVideoChunkCount)/100),
  1726. (WORD)(muldiv32(lpcs->dwFramesDropped,10000,lpcs->dwVideoChunkCount)%100)
  1727. );
  1728. }
  1729. }
  1730. EarlyExit:
  1731. //
  1732. // If we were compressing while capturing, close it down
  1733. //
  1734. if (lpcs->CompVars.hic) {
  1735. // Kludge, reset the lpBitsOut pointer
  1736. if (lpcs->CompVars.lpBitsOut)
  1737. ((LPBYTE) lpcs->CompVars.lpBitsOut) -= 8;
  1738. ICSeqCompressFrameEnd(&lpcs->CompVars);
  1739. }
  1740. if (fTryToPaint) {
  1741. if (hpalT)
  1742. SelectPalette(hdc, hpalT, FALSE);
  1743. ReleaseDC (lpcs->hwnd, hdc);
  1744. }
  1745. if (lpcs->sCapParms.fMCIControl)
  1746. MCIDeviceClose (lpcs);
  1747. // Let the user see where capture stopped
  1748. if ((!lpcs->fLiveWindow) && (!lpcs->fOverlayWindow))
  1749. videoFrame( lpcs->hVideoIn, &lpcs->VidHdr );
  1750. InvalidateRect( lpcs->hwnd, NULL, TRUE);
  1751. SetCursor(hOldCursor);
  1752. lpcs->fCapFileExists = (lpcs-> dwReturn == DV_ERR_OK);
  1753. lpcs->fCapturingNow = FALSE;
  1754. statusUpdateStatus(lpcs, IDS_CAP_END); // Always the last message
  1755. return;
  1756. }
  1757. // Returns TRUE if the capture task was created, or
  1758. // capture completed OK.
  1759. BOOL AVICapture (LPCAPSTREAM lpcs)
  1760. {
  1761. WORD w;
  1762. CAPINFOCHUNK cic;
  1763. char szSMPTE[40];
  1764. if (lpcs-> fCapturingNow)
  1765. return IDS_CAP_VIDEO_OPEN_ERROR;
  1766. lpcs-> fStopCapture = FALSE;
  1767. lpcs-> fAbortCapture = FALSE;
  1768. lpcs-> hTaskCapture = NULL;
  1769. lpcs-> dwReturn = 0;
  1770. // Clear any SMPTE info chunk
  1771. cic.fccInfoID = mmioFOURCC ('I','S','M','T');
  1772. cic.lpData = NULL;
  1773. cic.cbData = 0;
  1774. SetInfoChunk (lpcs, &cic);
  1775. #if 1
  1776. // And get ready to write a SMPTE info chunk
  1777. if (lpcs->sCapParms.fMCIControl) {
  1778. // create SMPTE string
  1779. TimeMSToSMPTE (lpcs->sCapParms.dwMCIStartTime, (LPSTR) szSMPTE);
  1780. cic.lpData = szSMPTE;
  1781. cic.cbData = lstrlen (szSMPTE) + 1;
  1782. SetInfoChunk (lpcs, &cic);
  1783. }
  1784. #endif
  1785. // Use an MCI device to do step capture capture???
  1786. if (lpcs->sCapParms.fStepMCIDevice && lpcs->sCapParms.fMCIControl) {
  1787. if (lpcs->sCapParms.fYield) {
  1788. w = (WORD) mmTaskCreate((LPTASKCALLBACK) MCIStepCapture,
  1789. &lpcs->hTaskCapture, (DWORD) lpcs);
  1790. // if task creation failed, turn off the capturing flag
  1791. if (w != 0)
  1792. lpcs->fCapturingNow = FALSE;
  1793. return ((BOOL) !w);
  1794. }
  1795. else {
  1796. MCIStepCapture (lpcs);
  1797. return ((BOOL) !lpcs->dwReturn);
  1798. }
  1799. }
  1800. // No MCI device, just a normal streaming capture
  1801. else if (lpcs->sCapParms.fYield) {
  1802. w = (WORD) mmTaskCreate((LPTASKCALLBACK) AVICapture1,
  1803. &lpcs->hTaskCapture, (DWORD) lpcs);
  1804. // if task creation failed, turn off the capturing flag
  1805. if (w != 0)
  1806. lpcs->fCapturingNow = FALSE;
  1807. return ((BOOL) !w);
  1808. }
  1809. else {
  1810. AVICapture1 (lpcs);
  1811. return ((BOOL) !lpcs->dwReturn);
  1812. }
  1813. }
  1814. 
  1815. 
  1816. 
  1817. 
  1818.