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.

1731 lines
54 KiB

  1. /****************************************************************************
  2. *
  3. * capio.c
  4. *
  5. * i/o routines for video 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 USE_AVIFILE 1
  19. #define JMK_HACK_DONTWRITE TRUE
  20. #define INC_OLE2
  21. #pragma warning(disable:4103)
  22. #include <windows.h>
  23. #include <windowsx.h>
  24. #include <win32.h>
  25. #include <mmsystem.h>
  26. #include <msvideo.h>
  27. #include <drawdib.h>
  28. #include <mmreg.h>
  29. #include <mmddk.h>
  30. #include "ivideo32.h"
  31. #include "mmdebug.h"
  32. #ifdef USE_ACM
  33. #include <msacm.h>
  34. #endif
  35. #include <avifmt.h>
  36. #include "avicap.h"
  37. #include "avicapi.h"
  38. #include "time.h"
  39. extern UINT GetSizeOfWaveFormat (LPWAVEFORMATEX lpwf);
  40. STATICFN BOOL IndexVideo (LPCAPSTREAM lpcs, DWORD dwSize, BOOL bKeyFrame);
  41. STATICFN BOOL IndexAudio (LPCAPSTREAM lpcs, DWORD dwSize);
  42. #ifdef _DEBUG
  43. #define DSTATUS(lpcs, sz) statusUpdateStatus(lpcs, IDS_CAP_INFO, (LPTSTR) TEXT(sz))
  44. #else
  45. #define DSTATUS(lpcs, sz)
  46. #endif
  47. #ifdef USE_AVIFILE
  48. VOID WINAPI AVIPreloadFat (LPCAPSTREAM lpcs)
  49. {
  50. return;
  51. }
  52. // Add an index entry for an audio buffer
  53. // dwSize is the size of data ONLY, not including the chunk or junk
  54. // Returns: TRUE if index space is not exhausted
  55. //
  56. STATICFN BOOL IndexAudio (LPCAPSTREAM lpcs, DWORD dwSize)
  57. {
  58. ++lpcs->dwWaveChunkCount;
  59. return TRUE;
  60. }
  61. // Add an index entry for a video frame
  62. // dwSize is the size of data ONLY, not including the chunk or junk
  63. // Returns: TRUE if index space is not exhausted
  64. //
  65. STATICFN BOOL IndexVideo (LPCAPSTREAM lpcs, DWORD dwSize, BOOL bKeyFrame)
  66. {
  67. ++lpcs->dwVideoChunkCount;
  68. return TRUE;
  69. }
  70. STATICFN void AVIFileCleanup(LPCAPSTREAM lpcs)
  71. {
  72. if (lpcs->paudio)
  73. AVIStreamClose(lpcs->paudio), lpcs->paudio = NULL;
  74. if (lpcs->pvideo)
  75. AVIStreamClose(lpcs->pvideo), lpcs->pvideo = NULL;
  76. if (lpcs->pavifile)
  77. AVIFileClose(lpcs->pavifile), lpcs->pavifile = NULL;
  78. }
  79. /*
  80. * CapFileInit
  81. *
  82. * Perform all initialization required to write a capture file.
  83. *
  84. * We take a slightly strange approach: We don't write
  85. * out the header until we're done capturing. For now,
  86. * we just seek 2K into the file, which is where all of
  87. * the real data will go.
  88. *
  89. * When we're done, we'll come back and write out the header,
  90. * because then we'll know all of the values we need.
  91. *
  92. * Also allocate and init the index.
  93. */
  94. BOOL CapFileInit (
  95. LPCAPSTREAM lpcs)
  96. {
  97. AVISTREAMINFOW si;
  98. LPBYTE ptr = NULL;
  99. UINT cksize;
  100. RGBQUAD FAR * prgb;
  101. PALETTEENTRY aPalEntry[256];
  102. LPBITMAPINFO lpBitsInfoOut; // Possibly compressed output format
  103. LONG lRet;
  104. UINT ii;
  105. // No special video format given -- use the default
  106. //
  107. lpBitsInfoOut = lpcs->CompVars.lpbiOut;
  108. if (lpcs->CompVars.hic == NULL)
  109. lpBitsInfoOut = lpcs->lpBitsInfo;
  110. // use avifile to access the data
  111. // create the avifile object, create a video and audio stream and
  112. // set the format for each stream.
  113. assert(lpcs->pavifile == NULL);
  114. /* if the capture file has not been set then error */
  115. if (!(*lpcs->achFile))
  116. goto error_exit;
  117. // !!! how to avoid truncating the file if already created ?
  118. lRet = AVIFileOpen(&lpcs->pavifile,
  119. lpcs->achFile,
  120. OF_WRITE | OF_CREATE,
  121. NULL);
  122. if (lRet || !lpcs->pavifile)
  123. goto error_exit;
  124. // create video stream
  125. ZeroMemory (&si, sizeof(si));
  126. si.fccType = streamtypeVIDEO;
  127. if (lpcs->CompVars.hic)
  128. si.fccHandler = lpcs->CompVars.fccHandler;
  129. else
  130. si.fccHandler = lpBitsInfoOut->bmiHeader.biCompression;
  131. // A bit of history...
  132. // In VFW 1.0, we set fccHandler to 0 for BI_RLE8 formats
  133. // as a kludge to make Mplayer and Videdit play the files.
  134. // Just prior to 1.1 release, we found this broke Premiere,
  135. // so now (after AVICAP beta is on Compuserve), we change the
  136. // fccHandler to "MRLE". Just ask Todd...
  137. // And now, at RC1, we change it again to "RLE ", Just ask Todd...
  138. if (si.fccHandler == BI_RLE8)
  139. si.fccHandler = mmioFOURCC('R', 'L', 'E', ' ');
  140. // !!!need to change this after capture
  141. si.dwScale = lpcs->sCapParms.dwRequestMicroSecPerFrame;
  142. si.dwRate = 1000000L;
  143. si.dwStart = 0L;
  144. si.dwQuality = (DWORD) -1L; /* !!! ICQUALITY_DEFAULT */
  145. si.dwSampleSize = 0L;
  146. lRet = AVIFileCreateStream(lpcs->pavifile, &lpcs->pvideo, &si);
  147. if (lRet || !lpcs->pvideo)
  148. goto error_exit;
  149. // set format of video stream
  150. // !!! dont write palette for full color?
  151. if (lpBitsInfoOut->bmiHeader.biBitCount > 8)
  152. lpBitsInfoOut->bmiHeader.biClrUsed = 0;
  153. // need to alloc a single block that we can fill with hdr + palette
  154. cksize = lpBitsInfoOut->bmiHeader.biSize
  155. + lpBitsInfoOut->bmiHeader.biClrUsed * sizeof(RGBQUAD);
  156. ptr = GlobalAllocPtr(GPTR, cksize);
  157. if (!ptr)
  158. goto error_exit;
  159. CopyMemory (ptr, (LPBYTE)&lpBitsInfoOut->bmiHeader,
  160. lpBitsInfoOut->bmiHeader.biSize);
  161. prgb = (RGBQUAD FAR *) &ptr[lpBitsInfoOut->bmiHeader.biSize];
  162. if (lpBitsInfoOut->bmiHeader.biClrUsed > 0) {
  163. // Get Palette info
  164. UINT nPalEntries = GetPaletteEntries(lpcs->hPalCurrent, 0,
  165. lpBitsInfoOut->bmiHeader.biClrUsed,
  166. aPalEntry);
  167. if (nPalEntries != lpBitsInfoOut->bmiHeader.biClrUsed)
  168. goto error_exit;
  169. for (ii = 0; ii < lpBitsInfoOut->bmiHeader.biClrUsed; ++ii) {
  170. prgb[ii].rgbRed = aPalEntry[ii].peRed;
  171. prgb[ii].rgbGreen = aPalEntry[ii].peGreen;
  172. prgb[ii].rgbBlue = aPalEntry[ii].peBlue;
  173. }
  174. }
  175. if (AVIStreamSetFormat(lpcs->pvideo, 0, ptr, cksize))
  176. goto error_exit;
  177. GlobalFreePtr(ptr), ptr = NULL;
  178. // create audio stream if sound capture enabled
  179. if (lpcs->sCapParms.fCaptureAudio) {
  180. ZeroMemory (&si, sizeof(si));
  181. si.fccType = streamtypeAUDIO;
  182. si.fccHandler = 0L;
  183. si.dwScale = lpcs->lpWaveFormat->nBlockAlign;
  184. si.dwRate = lpcs->lpWaveFormat->nAvgBytesPerSec;
  185. si.dwStart = 0L;
  186. si.dwLength = lpcs->dwWaveBytes / lpcs->lpWaveFormat->nBlockAlign;
  187. si.dwQuality = (DWORD)-1L; /* !!! ICQUALITY_DEFAULT */
  188. si.dwSampleSize = lpcs->lpWaveFormat->nBlockAlign;
  189. lRet = AVIFileCreateStream(lpcs->pavifile, &lpcs->paudio, &si);
  190. if (lRet || !lpcs->paudio)
  191. goto error_exit;
  192. // write wave stream format
  193. cksize = GetSizeOfWaveFormat (lpcs->lpWaveFormat);
  194. if (AVIStreamSetFormat(lpcs->paudio, 0, lpcs->lpWaveFormat, cksize))
  195. goto error_exit;
  196. }
  197. // start streaming
  198. //
  199. // parameters are random for now, and are not used at all by current impl.
  200. // probably covered by above call but you never know
  201. //
  202. AVIStreamBeginStreaming(lpcs->pvideo, 0, 32000, 1000);
  203. if (lpcs->sCapParms.fCaptureAudio)
  204. AVIStreamBeginStreaming(lpcs->paudio, 0, 32000, 1000);
  205. // this is used for timing calcs, not just indexing
  206. //
  207. lpcs->dwVideoChunkCount = 0;
  208. lpcs->dwWaveChunkCount = 0;
  209. // !!! write info chunks here
  210. return TRUE;
  211. error_exit:
  212. if (ptr) {
  213. GlobalFreePtr(ptr); ptr = NULL;
  214. }
  215. AVIFileCleanup (lpcs);
  216. return FALSE;
  217. }
  218. /*
  219. * AVIFileFini
  220. *
  221. * Write out the index, deallocate the index, and close the file.
  222. *
  223. */
  224. BOOL AVIFileFini (LPCAPSTREAM lpcs, BOOL fWroteJunkChunks, BOOL fAbort)
  225. {
  226. AVISTREAMINFOW si;
  227. DPF("AVICap32: Start of AVIFileFini\n");
  228. AVIStreamEndStreaming(lpcs->pvideo);
  229. if (lpcs->sCapParms.fCaptureAudio)
  230. AVIStreamEndStreaming(lpcs->paudio);
  231. // if we got a good file, allow editing of it
  232. lpcs->fFileCaptured = !fAbort;
  233. // -----------------------------------------------------------
  234. // adjust audio & video streams to be the same length
  235. // -----------------------------------------------------------
  236. #if 0 // old technique - match video to audio unconditionally
  237. // share the captured frames out evenly over the captured audio
  238. if (lpcs->sCapParms.fCaptureAudio && lpcs->dwVideoChunkCount &&
  239. (lpcs->dwWaveBytes > 0)) {
  240. /* HACK HACK */
  241. /* Set rate that was captured based on length of audio data */
  242. lpcs->dwActualMicroSecPerFrame = (DWORD)
  243. MulDiv((LONG)lpcs->dwWaveBytes,
  244. 1000000,
  245. (LONG)(lpcs->lpWaveFormat->nAvgBytesPerSec * lpcs->dwVideoChunkCount));
  246. } else {
  247. lpcs->dwActualMicroSecPerFrame = lpcs->sCapParms.dwRequestMicroSecPerFrame;
  248. }
  249. #else // new technique for stream length munging
  250. //
  251. // Init a value in case we're not capturing audio
  252. //
  253. lpcs->dwActualMicroSecPerFrame = lpcs->sCapParms.dwRequestMicroSecPerFrame;
  254. switch (lpcs->sCapParms.AVStreamMaster) {
  255. case AVSTREAMMASTER_NONE:
  256. lpcs->dwActualMicroSecPerFrame = lpcs->sCapParms.dwRequestMicroSecPerFrame;
  257. break;
  258. case AVSTREAMMASTER_AUDIO:
  259. default:
  260. // VFW 1.0 and 1.1 ALWAYS munged frame rate to match audio
  261. // duration.
  262. if (lpcs->sCapParms.fCaptureAudio && lpcs->dwVideoChunkCount) {
  263. // Modify the video framerate based on audio duration
  264. lpcs->dwActualMicroSecPerFrame = (DWORD)
  265. ((double)lpcs->dwWaveBytes * 1000000. /
  266. ((double)lpcs->lpWaveFormat->nAvgBytesPerSec *
  267. lpcs->dwVideoChunkCount + 0.5));
  268. }
  269. break;
  270. }
  271. #endif
  272. // ------------------------------------------------------------
  273. // write corrected stream timing back to the file
  274. // ------------------------------------------------------------
  275. #ifdef CHICAGO
  276. AVIStreamInfo (lpcs->pvideo, (LPAVISTREAMINFOA) &si, sizeof(si));
  277. #else
  278. AVIStreamInfo (lpcs->pvideo, &si, sizeof(si));
  279. #endif
  280. si.dwRate = 1000000L;
  281. si.dwScale = lpcs->dwActualMicroSecPerFrame;
  282. // no api for this- have to call the member directly!
  283. //
  284. lpcs->pvideo->lpVtbl->SetInfo(lpcs->pvideo, &si, sizeof(si));
  285. // Add the info chunks
  286. // This includes the capture card driver name, client app, date and time
  287. if (lpcs->lpInfoChunks) {
  288. LPBYTE lpInfo;
  289. DWORD cbData;
  290. lpInfo = lpcs->lpInfoChunks;
  291. while (lpInfo < (LPBYTE) lpcs->lpInfoChunks + lpcs->cbInfoChunks) {
  292. cbData = * (LPDWORD) (lpInfo + sizeof(DWORD));
  293. AVIFileWriteData (lpcs->pavifile,
  294. (DWORD) * (LPDWORD) lpInfo, // FOURCC
  295. lpInfo + sizeof (DWORD) * 2, // lpData
  296. cbData); // cbData
  297. lpInfo += cbData + sizeof (DWORD) * 2;
  298. }
  299. }
  300. AVIFileCleanup(lpcs);
  301. return lpcs->fFileCaptured;
  302. }
  303. //
  304. // Prepends dummy frame entries to the current valid video frame.
  305. // Bumps the index, but does not actually trigger a write operation.
  306. // nCount is a count of the number of frames to write
  307. // Returns: TRUE on a successful write
  308. BOOL WINAPI AVIWriteDummyFrames (
  309. LPCAPSTREAM lpcs,
  310. UINT nCount,
  311. LPUINT lpuError,
  312. LPBOOL lpbPending)
  313. {
  314. LONG lRet;
  315. lpcs->dwVideoChunkCount += nCount;
  316. lRet = AVIStreamWrite(lpcs->pvideo,
  317. -1, // current position
  318. nCount, // this many samples
  319. NULL, // no actual data
  320. 0, // no data
  321. 0, // not keyframe
  322. NULL, NULL); // no return of samples or bytes
  323. *lpbPending = FALSE;
  324. *lpuError = 0;
  325. if (lRet)
  326. *lpuError = IDS_CAP_FILE_WRITE_ERROR;
  327. return !(*lpuError);
  328. }
  329. // Writes compressed or uncompressed frames to the AVI file
  330. // returns TRUE if no error, FALSE if end of file.
  331. BOOL WINAPI AVIWriteVideoFrame (
  332. LPCAPSTREAM lpcs,
  333. LPBYTE lpData,
  334. DWORD dwBytesUsed,
  335. BOOL fKeyFrame,
  336. UINT uIndex,
  337. UINT nDropped,
  338. LPUINT lpuError,
  339. LPBOOL lpbPending)
  340. {
  341. LONG lRet;
  342. lRet = AVIStreamWrite(lpcs->pvideo, // write to video stream
  343. -1, // next sample
  344. 1, // 1 sample only
  345. lpData, // video buffer (no riff header)
  346. dwBytesUsed, // length of data
  347. fKeyFrame ? AVIIF_KEYFRAME : 0,
  348. NULL, NULL); // no return of sample or byte count
  349. *lpbPending = FALSE;
  350. *lpuError = 0;
  351. if (lRet)
  352. {
  353. dprintf("AVIStreamWrite returned 0x%x", lRet);
  354. *lpuError = IDS_CAP_FILE_WRITE_ERROR;
  355. }
  356. else
  357. {
  358. ++lpcs->dwVideoChunkCount;
  359. if (nDropped)
  360. AVIWriteDummyFrames (lpcs, nDropped, lpuError, lpbPending);
  361. }
  362. return !(*lpuError);
  363. }
  364. BOOL WINAPI AVIWriteAudio (
  365. LPCAPSTREAM lpcs,
  366. LPWAVEHDR lpWaveHdr,
  367. UINT uIndex,
  368. LPUINT lpuError,
  369. LPBOOL lpbPending)
  370. {
  371. LONG lRet;
  372. lRet = AVIStreamWrite(lpcs->paudio,
  373. -1, // next sample
  374. lpWaveHdr->dwBytesRecorded /
  375. lpcs->lpWaveFormat->nBlockAlign, // nr samples
  376. lpWaveHdr->lpData,
  377. lpWaveHdr->dwBytesRecorded,
  378. 0,
  379. NULL,
  380. NULL);
  381. *lpbPending = FALSE;
  382. *lpuError = 0;
  383. if (lRet)
  384. {
  385. dprintf("AVIStreamWrite returned 0x%x", lRet);
  386. *lpuError = IDS_CAP_FILE_WRITE_ERROR;
  387. }
  388. else
  389. ++lpcs->dwWaveChunkCount;
  390. return !(*lpuError);
  391. }
  392. #else //---------------- ! using Avifile ----------------------------
  393. // The following are anded with the size in the index
  394. #define IS_AUDIO_CHUNK 0x80000000
  395. #define IS_KEYFRAME_CHUNK 0x40000000
  396. #define IS_DUMMY_CHUNK 0x20000000
  397. #define IS_GRANULAR_CHUNK 0x10000000
  398. #define INDEX_MASK (IS_AUDIO_CHUNK | IS_KEYFRAME_CHUNK | IS_DUMMY_CHUNK | IS_GRANULAR_CHUNK)
  399. // Add an index entry for a video frame
  400. // dwSize is the size of data ONLY, not including the chunk or junk
  401. // Returns: TRUE if index space is not exhausted
  402. //
  403. STATICFN BOOL IndexVideo (LPCAPSTREAM lpcs, DWORD dwSize, BOOL bKeyFrame)
  404. {
  405. if (lpcs->dwIndex < lpcs->sCapParms.dwIndexSize) {
  406. *lpcs->lpdwIndexEntry = dwSize | (bKeyFrame ? IS_KEYFRAME_CHUNK : 0);
  407. ++lpcs->lpdwIndexEntry;
  408. ++lpcs->dwIndex;
  409. ++lpcs->dwVideoChunkCount;
  410. return TRUE;
  411. }
  412. dprintf("\n***WARNING*** Indexvideo space exhausted\n");
  413. return FALSE;
  414. }
  415. // Add an index entry for an audio buffer
  416. // dwSize is the size of data ONLY, not including the chunk or junk
  417. // Returns: TRUE if index space is not exhausted
  418. //
  419. STATICFN BOOL IndexAudio (LPCAPSTREAM lpcs, DWORD dwSize)
  420. {
  421. if (lpcs->dwIndex < lpcs->sCapParms.dwIndexSize) {
  422. *lpcs->lpdwIndexEntry = dwSize | IS_AUDIO_CHUNK;
  423. ++lpcs->lpdwIndexEntry;
  424. ++lpcs->dwIndex;
  425. ++lpcs->dwWaveChunkCount;
  426. return TRUE;
  427. }
  428. dprintf("\n***WARNING*** Indexaudio space exhausted\n");
  429. return FALSE;
  430. }
  431. DWORD CalcWaveBufferSize (LPCAPSTREAM lpcs)
  432. {
  433. DWORD dw;
  434. if (!lpcs->lpWaveFormat)
  435. return 0L;
  436. // at least .5 second
  437. dw = lpcs->lpWaveFormat->nAvgBytesPerSec / 2;
  438. if (lpcs->sCapParms.wChunkGranularity) {
  439. if (dw % lpcs->sCapParms.wChunkGranularity) {
  440. dw += lpcs->sCapParms.wChunkGranularity -
  441. dw % lpcs->sCapParms.wChunkGranularity;
  442. }
  443. }
  444. dw = max ((1024L * 16), dw); // at least 16K
  445. dw -= sizeof(RIFF);
  446. dprintf("Wave buffer size = %ld", dw);
  447. return dw;
  448. }
  449. /*
  450. * AVIPreloadFat
  451. *
  452. * Force FAT for this file to be loaded into the FAT cache
  453. *
  454. */
  455. VOID WINAPI AVIPreloadFat (LPCAPSTREAM lpcs)
  456. {
  457. DWORD dw;
  458. #ifdef CHICAGO
  459. DWORD dwPos;
  460. assert (lpcs->lpDropFrame);
  461. // save the current file pointer then seek to the end of the file
  462. //
  463. dwPos = SetFilePointer (lpcs->hFile, 0, NULL, FILE_CURRENT);
  464. dw = SetFilePointer (lpcs->hFile, 0, NULL, FILE_END);
  465. if ((dw == (DWORD)-1) || (dw < lpcs->dwBytesPerSector)) {
  466. // put the file pointer back to where it was
  467. SetFilePointer (lpcs->hFile, dwPos, NULL, FILE_BEGIN);
  468. return;
  469. }
  470. // read the last sector of the file, just to force
  471. // the fat for the file to be loaded
  472. //
  473. ReadFile (lpcs->hFile, lpcs->lpDropFrame, lpcs->dwBytesPerSector, &dw, NULL);
  474. // put the file pointer back to where it was
  475. //
  476. SetFilePointer (lpcs->hFile, dwPos, NULL, FILE_BEGIN);
  477. #else
  478. // Load all the FAT information. On NT this is sufficient for FAT
  479. // files. On NTFS partitiions there is no way we can read in all the
  480. // mapping information.
  481. GetFileSize(lpcs->hFile, &dw);
  482. #endif
  483. }
  484. #ifdef JMK_HACK_DONTWRITE
  485. static BOOL bDontWrite;
  486. #endif
  487. // Write data to the capture file
  488. // Returns: TRUE on a successful write
  489. //
  490. UINT NEAR PASCAL AVIWrite (
  491. LPCAPSTREAM lpcs,
  492. LPVOID pbuf,
  493. DWORD dwSize,
  494. UINT uIndex, // index of header for this buffer, -1 for step capture
  495. UINT uType,
  496. LPBOOL lpbPending)
  497. {
  498. DWORD dwWritten;
  499. DWORD dwGran;
  500. // the buffer must be sector aligned if using non-buffered IO
  501. // and the size must be at least word aligned
  502. // uIndex == -1 if this is a dummy frame write
  503. // uIndex == Index into alpVideoHdr OR index in alpWaveHdr based on uType
  504. //
  505. assert (!lpcs->fUsingNonBufferedIO || (!((DWORD)pbuf & (lpcs->dwBytesPerSector - 1))));
  506. assert (!(dwSize & 1));
  507. assert (dwSize);
  508. assert (*lpbPending == FALSE);
  509. // if we are doing non-buffered io, we need to pad each write
  510. // to an even multiple of sector size bytes, we do this by adding
  511. // a junk riff chunk into the write buffer after dwSize bytes
  512. //
  513. dwGran = lpcs->sCapParms.wChunkGranularity;
  514. if (lpcs->fUsingNonBufferedIO)
  515. dwGran = max (lpcs->dwBytesPerSector,
  516. (DWORD) lpcs->sCapParms.wChunkGranularity);
  517. assert (dwGran);
  518. if (dwSize % dwGran)
  519. {
  520. DWORD dwSizeT = dwGran - (dwSize % dwGran);
  521. LPRIFF priff = (LPRIFF)((LPBYTE)pbuf + dwSize + (dwSize & 1));
  522. if (dwSizeT < sizeof(RIFF))
  523. dwSizeT += dwGran;
  524. // add the junk riff chunk to the end of the buffer
  525. //
  526. priff->dwType = ckidAVIPADDING;
  527. priff->dwSize = dwSizeT - sizeof(RIFF);
  528. dwSize += dwSizeT;
  529. }
  530. #ifdef _DEBUG
  531. if (dwSize)
  532. {
  533. volatile BYTE bt;
  534. AuxDebugEx (8, DEBUGLINE "touch test of AviWrite buffer %08X\r\n", pbuf);
  535. bt = ((LPBYTE)pbuf)[dwSize-1];
  536. }
  537. // List all of the RIFF chunks within the block being written
  538. //
  539. dwWritten = 0;
  540. while (dwWritten < dwSize)
  541. {
  542. LPRIFF priff = (LPVOID)((LPBYTE)pbuf + dwWritten);
  543. AuxDebugEx (4, DEBUGLINE "RIFF=%.4s size=%08X\r\n",
  544. &priff->dwType, priff->dwSize);
  545. dwWritten += priff->dwSize + sizeof(RIFF);
  546. }
  547. #endif
  548. // BUGBUG, Remove the following line when done performance testing
  549. #ifdef JMK_HACK_DONTWRITE
  550. if (bDontWrite)
  551. return 0;
  552. #endif
  553. if (lpcs->pAsync)
  554. {
  555. struct _avi_async * lpah = &lpcs->pAsync[lpcs->iLastAsync];
  556. UINT iLastAsync;
  557. // set iLastAsync to point to what lpcs->iLastAsync
  558. // would be if we were to increment it. If we end up
  559. // with an i/o that does not complete synchronously
  560. // we will then update lpcs->iLastAsync so that we can
  561. // remember to check for completion later
  562. //
  563. if ((iLastAsync = lpcs->iLastAsync+1) >= lpcs->iNumAsync)
  564. iLastAsync = 0;
  565. // is the async buffer that we are trying to use
  566. // already in use?
  567. //
  568. if (iLastAsync == lpcs->iNextAsync) {
  569. AuxDebugEx(1, DEBUGLINE "async buffer already in use\r\n");
  570. return IDS_CAP_FILE_WRITE_ERROR;
  571. }
  572. assert (!lpah->uType);
  573. // init the async buffer with the info that we will need
  574. // to release the buffer when the io is complete
  575. //
  576. ZeroMemory (&lpah->ovl, sizeof(lpah->ovl));
  577. lpah->ovl.hEvent = lpcs->hCaptureEvent;
  578. lpah->ovl.Offset = lpcs->dwAsyncWriteOffset;
  579. // attempt an async write. if WriteFile fails, we then
  580. // need to check if it's a real failure, or just an instance
  581. // of delayed completion. if delayed completion, we fill out
  582. // the lpah structure so that we know what buffer to re-use
  583. // when the io finally completes.
  584. //
  585. if ( ! WriteFile (lpcs->hFile, pbuf, dwSize, &dwWritten, &lpah->ovl))
  586. {
  587. if (GetLastError() == ERROR_IO_PENDING)
  588. {
  589. // if we are passed a index of -1, that means that
  590. // this buffer is not associated with any entry in the
  591. // header array. in this case, we must have the io complete
  592. // before we return from this function.
  593. //
  594. if (uIndex == (UINT)-1)
  595. {
  596. while (1) {
  597. if ( ! GetOverlappedResult (lpcs->hFile, &lpah->ovl, &dwWritten, TRUE))
  598. {
  599. AuxDebugEx (0, DEBUGLINE "WriteFile failed %d\r\n", GetLastError());
  600. return IDS_CAP_FILE_WRITE_ERROR;
  601. }
  602. // if Internal is non-zero, the io is still pending
  603. // NOTE: this needs to be changed as soon as possible.
  604. // The current state of play means that GetOverlappedResult CAN return
  605. // something other than ERROR_IO_COMPLETE and the IO will still be
  606. // pending. This causes us to make the wrong decision.
  607. if (lpah->ovl.Internal != 0) {
  608. Sleep(100); // release some cycles
  609. }
  610. else
  611. break;
  612. }
  613. //#pragma message(SQUAWK "get rid of this hack when GetOverlappedResult is fixed!")
  614. }
  615. else
  616. {
  617. // io is begun, but not yet completed. so setup info in
  618. // the pending io array so that we can check later for completion
  619. //
  620. *lpbPending = TRUE;
  621. lpah->uType = uType;
  622. lpah->uIndex = uIndex;
  623. AuxDebugEx(2, DEBUGLINE "IOPending... iLastAsync was %d, will be %d, uIndex=%d, Event=%d\r\n",lpcs->iLastAsync , iLastAsync, uIndex, lpah->ovl.hEvent);
  624. lpcs->iLastAsync = iLastAsync;
  625. }
  626. }
  627. else
  628. {
  629. AuxDebugEx (0, DEBUGLINE "WriteFile failed %d\r\n", GetLastError());
  630. return IDS_CAP_FILE_WRITE_ERROR;
  631. }
  632. }
  633. // we get to here only when the io succeeds or is pending
  634. // so update the seek offset for use in the next write operation
  635. //
  636. lpcs->dwAsyncWriteOffset += dwSize;
  637. }
  638. else
  639. {
  640. // We are writing synchronously to the file
  641. if (!WriteFile (lpcs->hFile, pbuf, dwSize, &dwWritten, NULL) ||
  642. !(dwWritten == dwSize))
  643. return IDS_CAP_FILE_WRITE_ERROR;
  644. }
  645. return 0;
  646. }
  647. /*
  648. * CapFileInit
  649. *
  650. * Perform all initialization required to write a capture file.
  651. *
  652. * We take a slightly strange approach: We don't write
  653. * out the header until we're done capturing. For now,
  654. * we just seek 2K into the file, which is where all of
  655. * the real data will go.
  656. *
  657. * When we're done, we'll come back and write out the header,
  658. * because then we'll know all of the values we need.
  659. *
  660. * Also allocate and init the index.
  661. */
  662. BOOL CapFileInit (LPCAPSTREAM lpcs)
  663. {
  664. LONG l;
  665. LPBITMAPINFO lpBitsInfoOut; // Possibly compressed output format
  666. DWORD dwOpenFlags;
  667. // No special video format given -- use the default
  668. lpBitsInfoOut = lpcs->CompVars.lpbiOut;
  669. if (lpcs->CompVars.hic == NULL)
  670. lpBitsInfoOut = lpcs->lpBitsInfo;
  671. assert (lpcs->hmmio == NULL); // Should never have a file handle on entry
  672. // if the capture file has not been set then set it now
  673. if (!(*lpcs->achFile))
  674. goto INIT_FILE_OPEN_ERROR;
  675. // Get the Bytes per sector for the drive
  676. {
  677. DWORD dwSectorsPerCluster;
  678. DWORD dwFreeClusters;
  679. DWORD dwClusters;
  680. TCHAR szFullPathName[MAX_PATH];
  681. LPTSTR pszFilePart;
  682. GetFullPathName (lpcs->achFile,
  683. NUMELMS (szFullPathName),
  684. szFullPathName,
  685. &pszFilePart);
  686. if (szFullPathName[1] == TEXT(':') && szFullPathName[2] == TEXT('\\')) {
  687. szFullPathName[3] = TEXT('\0'); // Terminate after "x:\"
  688. GetDiskFreeSpace (szFullPathName,
  689. &dwSectorsPerCluster,
  690. &lpcs->dwBytesPerSector,
  691. &dwFreeClusters,
  692. &dwClusters);
  693. AuxDebugEx (3, DEBUGLINE "BytesPerSector=%d\r\n",
  694. lpcs->dwBytesPerSector);
  695. }
  696. else {
  697. // This handles cases where we do not have a "x:\" filename
  698. // Principally this will be "\\server\name\path..."
  699. lpcs->dwBytesPerSector = DEFAULT_BYTESPERSECTOR;
  700. AuxDebugEx (3, DEBUGLINE "FullPath=%s\r\n", szFullPathName);
  701. AuxDebugEx (3, DEBUGLINE "GetFullPath failed, Forcing dwBytesPerSector to %d\r\n",DEFAULT_BYTESPERSECTOR);
  702. }
  703. // bytes per sector must be non-zero and a power of two
  704. //
  705. assert (lpcs->dwBytesPerSector);
  706. assert (!(lpcs->dwBytesPerSector & (lpcs->dwBytesPerSector-1)));
  707. }
  708. #ifdef ZERO_THE_FILE_FOR_TESTING
  709. {
  710. char c[64 * 1024];
  711. DWORD dwSize;
  712. DWORD dwBW;
  713. // Open the file just to zero it
  714. lpcs->hFile = CreateFile (lpcs->achFile,
  715. GENERIC_READ | GENERIC_WRITE,
  716. FILE_SHARE_READ,
  717. NULL,
  718. OPEN_ALWAYS,
  719. FILE_ATTRIBUTE_NORMAL,
  720. NULL);
  721. if (lpcs->hFile == INVALID_HANDLE_VALUE) {
  722. lpcs->hFile = 0;
  723. goto INIT_FILE_OPEN_ERROR;
  724. }
  725. ZeroMemory (c, sizeof(c));
  726. SetFilePointer (lpcs->hFile, 0, NULL, FILE_BEGIN);
  727. dwSize = GetFileSize (lpcs->hFile, NULL);
  728. while (SetFilePointer (lpcs->hFile, 0, NULL, FILE_CURRENT) < dwSize)
  729. WriteFile (lpcs->hFile, c, sizeof(c), &dwBW, NULL);
  730. }
  731. CloseHandle(lpcs->hFile); // Close the "normal" open
  732. #endif
  733. // We can use non-buffered I/O if the ChunkGranularity is
  734. // a multiple of BytesPerSector. Better check that wChunkGranularity
  735. // has indeed been set
  736. if (0 == lpcs->sCapParms.wChunkGranularity)
  737. lpcs->sCapParms.wChunkGranularity = lpcs->dwBytesPerSector;
  738. lpcs->fUsingNonBufferedIO =
  739. (lpcs->sCapParms.wChunkGranularity >= lpcs->dwBytesPerSector) &&
  740. ((lpcs->sCapParms.wChunkGranularity % lpcs->dwBytesPerSector) == 0) &&
  741. (lpcs->CompVars.hic == NULL) &&
  742. (!(lpcs->fCaptureFlags & CAP_fStepCapturingNow)) &&
  743. (!(lpcs->fCaptureFlags & CAP_fFrameCapturingNow));
  744. AuxDebugEx (3, DEBUGLINE "fUsingNonBufferedIO=%d\r\n", lpcs->fUsingNonBufferedIO);
  745. // setup CreateFile flags based on whether we are using
  746. // non-buffered io and/or overlapped io
  747. //
  748. dwOpenFlags = FILE_ATTRIBUTE_NORMAL;
  749. if (lpcs->fUsingNonBufferedIO)
  750. {
  751. dwOpenFlags |= FILE_FLAG_NO_BUFFERING;
  752. #ifdef CHICAGO
  753. #pragma message (SQUAWK "find a better way to set AsyncIO flag")
  754. if (GetProfileIntA ("Avicap32", "AsyncIO", FALSE))
  755. #else
  756. // give a way to override the async default option.
  757. if (!GetProfileIntA ("Avicap32", "AsyncIO", TRUE)) {
  758. AuxDebugEx (2, DEBUGLINE "NOT doing Async IO\r\n");
  759. } else
  760. #endif
  761. {
  762. AuxDebugEx (3, DEBUGLINE "Doing Async IO\r\n");
  763. dwOpenFlags |= FILE_FLAG_OVERLAPPED;
  764. // We are requested to do async io. Allocate an array
  765. // of async io headers and initialize the async io fields
  766. // in the CAPSTREAM structure
  767. //
  768. {
  769. UINT iNumAsync = NUMELMS(lpcs->alpVideoHdr) + NUMELMS(lpcs->alpWaveHdr) + 2;
  770. // This is quite a lot of buffers. Perhaps we should limit
  771. // ourselves to lpcs->iNumVideo and lpcs->iNumAudio EXCEPT
  772. // these fields have not yet been set up. We would need
  773. // to look in the cap stream structure to get the information.
  774. // It is simpler to assume the maximum numbers.
  775. lpcs->dwAsyncWriteOffset = lpcs->dwAVIHdrSize;
  776. lpcs->iNextAsync = lpcs->iLastAsync = 0;
  777. lpcs->pAsync = GlobalAllocPtr (GMEM_MOVEABLE | GMEM_ZEROINIT,
  778. sizeof(*lpcs->pAsync) * iNumAsync);
  779. if (lpcs->pAsync) {
  780. lpcs->iNumAsync = iNumAsync;
  781. } else {
  782. // cannot allocate the memory. Go synchronous
  783. dprintf("Failed to allocate async buffers");
  784. dwOpenFlags &= ~(FILE_FLAG_OVERLAPPED);
  785. }
  786. }
  787. }
  788. }
  789. // Open the capture file, using Non Buffered I/O
  790. // if possible, given sector size, and buffer granularity
  791. //
  792. lpcs->hFile = CreateFile (lpcs->achFile,
  793. GENERIC_READ | GENERIC_WRITE,
  794. FILE_SHARE_READ,
  795. NULL,
  796. OPEN_ALWAYS,
  797. dwOpenFlags,
  798. NULL);
  799. if (lpcs->hFile == INVALID_HANDLE_VALUE) {
  800. lpcs->hFile = 0;
  801. goto INIT_FILE_OPEN_ERROR;
  802. }
  803. // BUGBUG, Remove the following line when done performance testing
  804. #ifdef JMK_HACK_DONTWRITE
  805. bDontWrite = GetProfileIntA("AVICAP32", "DontWrite", FALSE);
  806. #endif
  807. // Seek to a multiple of ChunkGranularity + AVIHEADERSIZE.
  808. // This is the point at which we'll start writing
  809. // Later, we'll come back and fill in the AVI header and index.
  810. // l is zero for standard wave and video formats
  811. l = (GetSizeOfWaveFormat ((LPWAVEFORMATEX) lpcs->lpWaveFormat) -
  812. sizeof (PCMWAVEFORMAT)) +
  813. (lpBitsInfoOut->bmiHeader.biSize -
  814. sizeof (BITMAPINFOHEADER));
  815. // (2K + size of wave and video stream headers) rounded to next 2K
  816. lpcs->dwAVIHdrSize = AVI_HEADERSIZE +
  817. (((lpcs->cbInfoChunks + l + lpcs->sCapParms.wChunkGranularity - 1)
  818. / lpcs->sCapParms.wChunkGranularity) * lpcs->sCapParms.wChunkGranularity);
  819. dprintf("AVIHdrSize = %ld", lpcs->dwAVIHdrSize);
  820. SetFilePointer (lpcs->hFile, lpcs->dwAVIHdrSize, NULL, FILE_BEGIN);
  821. if (lpcs->pAsync)
  822. lpcs->dwAsyncWriteOffset = lpcs->dwAVIHdrSize;
  823. // do all Index allocations
  824. if (!InitIndex (lpcs))
  825. CloseHandle (lpcs->hFile), lpcs->hFile = 0;
  826. lpcs->dwVideoChunkCount = 0;
  827. lpcs->dwWaveChunkCount = 0;
  828. INIT_FILE_OPEN_ERROR:
  829. if (lpcs->hFile) {
  830. return(TRUE);
  831. }
  832. if (lpcs->pAsync) {
  833. GlobalFreePtr(lpcs->pAsync), lpcs->pAsync=NULL;
  834. }
  835. return (FALSE);
  836. }
  837. ///////////////////////////////////////////////////////////////////////////
  838. // The index array is used to record the positions
  839. // of every chunk in the RIFF (avi) file.
  840. //
  841. // what this array is:
  842. //
  843. // each entry contains the size of the data
  844. // high order bits encode the type of data (audio / video)
  845. // and whether the video chunk is a key frame, dropped frame, etc.
  846. ///////////////////////////////////////////////////////////////////////////
  847. // Allocate the index table
  848. // Returns: TRUE if index can be allocated
  849. //
  850. BOOL InitIndex (LPCAPSTREAM lpcs)
  851. {
  852. lpcs->dwIndex = 0;
  853. // we assume that we have not already allocated an index
  854. //
  855. assert (lpcs->lpdwIndexStart == NULL);
  856. // Limit index size between 1 minute at 30fps and 3 hours at 30fps
  857. lpcs->sCapParms.dwIndexSize = max (lpcs->sCapParms.dwIndexSize, 1800);
  858. lpcs->sCapParms.dwIndexSize = min (lpcs->sCapParms.dwIndexSize, 324000L);
  859. dprintf("Max Index Size = %ld", lpcs->sCapParms.dwIndexSize);
  860. if (lpcs->hIndex = GlobalAlloc (GMEM_MOVEABLE,
  861. lpcs->sCapParms.dwIndexSize * sizeof (DWORD))) {
  862. if (lpcs->lpdwIndexEntry =
  863. lpcs->lpdwIndexStart = (LPDWORD)GlobalLock (lpcs->hIndex))
  864. return TRUE; // Success
  865. GlobalFree (lpcs->hIndex);
  866. lpcs->hIndex = NULL;
  867. }
  868. lpcs->lpdwIndexStart = NULL;
  869. return FALSE;
  870. }
  871. // Deallocate the index table
  872. //
  873. void FiniIndex (LPCAPSTREAM lpcs)
  874. {
  875. if (lpcs->hIndex) {
  876. if (lpcs->lpdwIndexStart)
  877. GlobalUnlock (lpcs->hIndex);
  878. GlobalFree (lpcs->hIndex);
  879. lpcs->hIndex = NULL;
  880. }
  881. lpcs->lpdwIndexStart = NULL;
  882. }
  883. // Write out the index at the end of the capture file.
  884. // The single frame capture methods do not append
  885. // JunkChunks! Audio chunks also now may have junk appended.
  886. //
  887. BOOL WriteIndex (LPCAPSTREAM lpcs, BOOL fJunkChunkWritten)
  888. {
  889. BOOL fChunkIsAudio;
  890. BOOL fChunkIsKeyFrame;
  891. BOOL fChunkIsDummy;
  892. BOOL fChunkIsGranular;
  893. DWORD dwIndex;
  894. DWORD dw;
  895. DWORD dwJunk;
  896. DWORD off;
  897. AVIINDEXENTRY avii;
  898. MMCKINFO ck;
  899. LPDWORD lpdw;
  900. DWORD dwGran;
  901. // determine which granularity (if any) to use
  902. // when calulating junk appended
  903. //
  904. dwGran = 0;
  905. if (fJunkChunkWritten)
  906. {
  907. dwGran = lpcs->sCapParms.wChunkGranularity;
  908. if (lpcs->fUsingNonBufferedIO)
  909. dwGran = max (lpcs->dwBytesPerSector, dwGran);
  910. }
  911. if (lpcs->dwIndex > lpcs->sCapParms.dwIndexSize)
  912. return TRUE;
  913. off = lpcs->dwAVIHdrSize;
  914. ck.cksize = 0;
  915. ck.ckid = ckidAVINEWINDEX;
  916. ck.fccType = 0;
  917. if (mmioCreateChunk(lpcs->hmmio,&ck,0)) {
  918. dprintf("Failed to create chunk for index");
  919. return FALSE;
  920. }
  921. lpdw = lpcs->lpdwIndexStart;
  922. for (dwIndex= 0; dwIndex < lpcs->dwIndex; dwIndex++) {
  923. dw = *lpdw++;
  924. fChunkIsAudio = (BOOL) ((dw & IS_AUDIO_CHUNK) != 0);
  925. fChunkIsKeyFrame = (BOOL) ((dw & IS_KEYFRAME_CHUNK) != 0);
  926. fChunkIsDummy = (BOOL) ((dw & IS_DUMMY_CHUNK) != 0);
  927. fChunkIsGranular = (BOOL) ((dw & IS_GRANULAR_CHUNK) != 0);
  928. dw &= ~(INDEX_MASK);
  929. if (fChunkIsAudio) {
  930. avii.ckid = MAKEAVICKID(cktypeWAVEbytes, 1);
  931. avii.dwFlags = 0;
  932. } else {
  933. if (lpcs->lpBitsInfo->bmiHeader.biCompression == BI_RLE8)
  934. avii.ckid = MAKEAVICKID(cktypeDIBcompressed, 0);
  935. else
  936. avii.ckid = MAKEAVICKID(cktypeDIBbits, 0);
  937. avii.dwFlags = fChunkIsKeyFrame ? AVIIF_KEYFRAME : 0;
  938. }
  939. avii.dwChunkLength = dw;
  940. avii.dwChunkOffset = off;
  941. if (mmioWrite(lpcs->hmmio, (LPVOID)&avii, sizeof(avii)) != sizeof(avii)) {
  942. dprintf("Failed to write index chunk %d", dwIndex);
  943. return FALSE;
  944. }
  945. dw += sizeof (RIFF);
  946. // round to word boundary
  947. //
  948. dw += (dw & 1);
  949. off += dw;
  950. // If a Junk chunk was appended, move past it
  951. //
  952. if (fChunkIsGranular && dwGran && (off % dwGran)) {
  953. dwJunk = dwGran - (off % dwGran);
  954. if (dwJunk < sizeof (RIFF))
  955. off += dwGran;
  956. off += dwJunk;
  957. }
  958. }
  959. if (mmioAscend(lpcs->hmmio, &ck, 0)){
  960. dprintf("Failed to ascend at end of index writing");
  961. return FALSE;
  962. }
  963. return TRUE;
  964. }
  965. /*
  966. * AVIFileFini
  967. *
  968. * Write out the index, deallocate the index, and close the file.
  969. *
  970. */
  971. BOOL AVIFileFini (LPCAPSTREAM lpcs, BOOL fWroteJunkChunks, BOOL fAbort)
  972. {
  973. MMCKINFO ckRiff;
  974. MMCKINFO ckList;
  975. MMCKINFO ckStream;
  976. MMCKINFO ck;
  977. UINT ii;
  978. DWORD dw;
  979. AVIStreamHeader strhdr;
  980. DWORD dwDataEnd;
  981. BOOL fRet = TRUE;
  982. RGBQUAD argbq[256];
  983. MainAVIHeader aviHdr;
  984. BOOL fSound;
  985. LPBITMAPINFO lpBitsInfoOut; // Possibly compressed output format
  986. // No special video format given -- use the default
  987. //
  988. lpBitsInfoOut = lpcs->lpBitsInfo;
  989. #ifdef NEW_COMPMAN
  990. if (lpcs->CompVars.hic != NULL)
  991. lpBitsInfoOut = lpcs->CompVars.lpbiOut;
  992. #endif
  993. // if the capture file has not been opened, we have nothing to do
  994. //
  995. if (lpcs->hFile == 0)
  996. return FALSE;
  997. // save off the current seek position. this is the end of the capture
  998. // data. then close the capture file, we will do the final work
  999. // on the capture file using mmio & buffered io.
  1000. //
  1001. if (lpcs->pAsync)
  1002. dwDataEnd = lpcs->dwAsyncWriteOffset;
  1003. else
  1004. dwDataEnd = SetFilePointer (lpcs->hFile, 0, NULL, FILE_CURRENT);
  1005. CloseHandle (lpcs->hFile), lpcs->hFile = 0;
  1006. // if we had allocated space for async buffers, free them now
  1007. //
  1008. if (lpcs->pAsync)
  1009. {
  1010. GlobalFreePtr (lpcs->pAsync);
  1011. lpcs->pAsync = NULL;
  1012. lpcs->iNextAsync = lpcs->iLastAsync = lpcs->iNumAsync = 0;
  1013. }
  1014. // if we are aborting capture, we are done
  1015. lpcs->hmmio = mmioOpen(lpcs->achFile, NULL, MMIO_WRITE);
  1016. assert (lpcs->hmmio != NULL);
  1017. //
  1018. if (fAbort)
  1019. goto FileError;
  1020. if (!lpcs->dwWaveBytes)
  1021. fSound = FALSE;
  1022. else
  1023. fSound = lpcs->sCapParms.fCaptureAudio && (!(lpcs->fCaptureFlags & CAP_fFrameCapturingNow));
  1024. // Seek to beginning of file, so we can write the header.
  1025. mmioSeek(lpcs->hmmio, 0, SEEK_SET);
  1026. DSTATUS(lpcs, "Writing AVI header");
  1027. // Create RIFF chunk
  1028. ckRiff.cksize = 0;
  1029. ckRiff.fccType = formtypeAVI;
  1030. if (mmioCreateChunk(lpcs->hmmio,&ckRiff,MMIO_CREATERIFF))
  1031. goto FileError;
  1032. // Create header list
  1033. ckList.cksize = 0;
  1034. ckList.fccType = listtypeAVIHEADER;
  1035. if (mmioCreateChunk(lpcs->hmmio,&ckList,MMIO_CREATELIST))
  1036. goto FileError;
  1037. // Create AVI header chunk
  1038. ck.cksize = sizeof(MainAVIHeader);
  1039. ck.ckid = ckidAVIMAINHDR;
  1040. if (mmioCreateChunk(lpcs->hmmio,&ck,0))
  1041. goto FileError;
  1042. lpcs->dwAVIHdrPos = ck.dwDataOffset;
  1043. // Calculate AVI header info
  1044. //
  1045. ZeroMemory (&aviHdr, sizeof(aviHdr));
  1046. //
  1047. // Set the stream lengths based on the Master stream
  1048. //
  1049. #if 0 // stream length calc with unconditional audio master
  1050. aviHdr.dwMicroSecPerFrame = lpcs->sCapParms.dwRequestMicroSecPerFrame;
  1051. if (fSound && lpcs->dwVideoChunkCount) {
  1052. /* HACK HACK */
  1053. /* Set rate that was captured based on length of audio data */
  1054. aviHdr.dwMicroSecPerFrame = (DWORD) MulDiv ((LONG)lpcs->dwWaveBytes,
  1055. 1000000,
  1056. (LONG)(lpcs->lpWaveFormat->nAvgBytesPerSec * lpcs->dwVideoChunkCount));
  1057. }
  1058. #else
  1059. // Init a value in case we're not capturing audio
  1060. aviHdr.dwMicroSecPerFrame = lpcs->sCapParms.dwRequestMicroSecPerFrame;
  1061. switch (lpcs->sCapParms.AVStreamMaster) {
  1062. case AVSTREAMMASTER_NONE:
  1063. break;
  1064. case AVSTREAMMASTER_AUDIO:
  1065. default:
  1066. // VFW 1.0 and 1.1 ALWAYS munged frame rate to match audio
  1067. // duration.
  1068. if (fSound && lpcs->sCapParms.fCaptureAudio && lpcs->dwVideoChunkCount) {
  1069. // Modify the video framerate based on audio duration
  1070. aviHdr.dwMicroSecPerFrame = (DWORD)
  1071. ((double)lpcs->dwWaveBytes * 1000000. /
  1072. ((double)lpcs->lpWaveFormat->nAvgBytesPerSec *
  1073. lpcs->dwVideoChunkCount + 0.5));
  1074. }
  1075. break;
  1076. }
  1077. #endif
  1078. lpcs->dwActualMicroSecPerFrame = aviHdr.dwMicroSecPerFrame;
  1079. aviHdr.dwMaxBytesPerSec = (DWORD) MulDiv (lpBitsInfoOut->bmiHeader.biSizeImage,
  1080. 1000000,
  1081. lpcs->sCapParms.dwRequestMicroSecPerFrame) +
  1082. (fSound ? lpcs->lpWaveFormat->nAvgBytesPerSec : 0);
  1083. aviHdr.dwPaddingGranularity = 0L;
  1084. aviHdr.dwFlags = AVIF_WASCAPTUREFILE | AVIF_HASINDEX;
  1085. aviHdr.dwStreams = fSound ? 2 : 1;
  1086. aviHdr.dwTotalFrames = lpcs->dwVideoChunkCount;
  1087. aviHdr.dwInitialFrames = 0L;
  1088. aviHdr.dwSuggestedBufferSize = 0L;
  1089. aviHdr.dwWidth = lpBitsInfoOut->bmiHeader.biWidth;
  1090. aviHdr.dwHeight = lpBitsInfoOut->bmiHeader.biHeight;
  1091. aviHdr.dwReserved[0] = 0;
  1092. aviHdr.dwReserved[1] = 0;
  1093. aviHdr.dwReserved[2] = 0;
  1094. aviHdr.dwReserved[3] = 0;
  1095. //aviHdr.dwRate = 1000000L;
  1096. //aviHdr.dwScale = aviHdr.dwMicroSecPerFrame;
  1097. //aviHdr.dwStart = 0L;
  1098. //aviHdr.dwLength = lpcs->dwVideoChunkCount;
  1099. // Write AVI header info
  1100. if (mmioWrite(lpcs->hmmio, (LPBYTE)&aviHdr, sizeof(aviHdr)) != sizeof(aviHdr) ||
  1101. mmioAscend(lpcs->hmmio, &ck, 0))
  1102. goto FileError;
  1103. DSTATUS(lpcs, "Writing AVI Stream header");
  1104. // Create stream header list
  1105. ckStream.cksize = 0;
  1106. ckStream.fccType = listtypeSTREAMHEADER;
  1107. if (mmioCreateChunk(lpcs->hmmio,&ckStream,MMIO_CREATELIST))
  1108. goto FileError;
  1109. ZeroMemory (&strhdr, sizeof(strhdr));
  1110. strhdr.fccType = streamtypeVIDEO;
  1111. strhdr.fccHandler = lpBitsInfoOut->bmiHeader.biCompression;
  1112. #ifdef NEW_COMPMAN
  1113. if (lpcs->CompVars.hic)
  1114. strhdr.fccHandler = lpcs->CompVars.fccHandler;
  1115. #endif
  1116. // A bit of history...
  1117. // In VFW 1.0, we set fccHandler to 0 for BI_RLE8 formats
  1118. // as a kludge to make Mplayer and Videdit play the files.
  1119. // Just prior to 1.1 release, we found this broke Premiere,
  1120. // so now (after AVICAP beta is on Compuserve), we change the
  1121. // fccHandler to "MRLE". Just ask Todd...
  1122. // And now, at RC1, we change it again to "RLE ", Just ask Todd...
  1123. if (strhdr.fccHandler == BI_RLE8)
  1124. strhdr.fccHandler = mmioFOURCC('R', 'L', 'E', ' ');
  1125. //strhdr.dwFlags = 0L;
  1126. #ifdef NEW_COMPMAN
  1127. //strhdr.wPriority = 0L;
  1128. //strhdr.wLanguage = 0L;
  1129. #else
  1130. //strhdr.dwPriority = 0L;
  1131. #endif
  1132. //strhdr.dwInitialFrames = 0L;
  1133. strhdr.dwScale = aviHdr.dwMicroSecPerFrame;
  1134. strhdr.dwRate = 1000000L;
  1135. //strhdr.dwStart = 0L;
  1136. strhdr.dwLength = lpcs->dwVideoChunkCount; /* Needs to get filled in! */
  1137. strhdr.dwQuality = (DWORD) -1L; /* !!! ICQUALITY_DEFAULT */
  1138. //strhdr.dwSampleSize = 0L;
  1139. //
  1140. // Write stream header data
  1141. //
  1142. ck.ckid = ckidSTREAMHEADER;
  1143. if (mmioCreateChunk(lpcs->hmmio,&ck,0) ||
  1144. mmioWrite(lpcs->hmmio, (LPBYTE)&strhdr, sizeof(strhdr)) != sizeof(strhdr) ||
  1145. mmioAscend(lpcs->hmmio, &ck, 0))
  1146. goto FileError;
  1147. /*
  1148. ** !!! dont write palette for full color?
  1149. */
  1150. if (lpBitsInfoOut->bmiHeader.biBitCount > 8)
  1151. lpBitsInfoOut->bmiHeader.biClrUsed = 0;
  1152. /* Create DIB header chunk */
  1153. ck.cksize = lpBitsInfoOut->bmiHeader.biSize +
  1154. lpBitsInfoOut->bmiHeader.biClrUsed *
  1155. sizeof(RGBQUAD);
  1156. ck.ckid = ckidSTREAMFORMAT;
  1157. if (mmioCreateChunk(lpcs->hmmio,&ck,0))
  1158. goto FileError;
  1159. /* Write DIB header data */
  1160. if (mmioWrite(lpcs->hmmio, (LPBYTE)&lpBitsInfoOut->bmiHeader,
  1161. lpBitsInfoOut->bmiHeader.biSize) !=
  1162. (LONG) lpBitsInfoOut->bmiHeader.biSize)
  1163. goto FileError;
  1164. if (lpBitsInfoOut->bmiHeader.biClrUsed > 0) {
  1165. // Get Palette info
  1166. if ((ii = GetPaletteEntries(lpcs->hPalCurrent, 0,
  1167. (UINT) lpBitsInfoOut->bmiHeader.biClrUsed,
  1168. (LPPALETTEENTRY) argbq)) !=
  1169. (UINT)lpBitsInfoOut->bmiHeader.biClrUsed)
  1170. goto FileError;
  1171. // Reorder the palette from PALETTEENTRY order to RGBQUAD order
  1172. // by swapping the red and blue palette entries.
  1173. //for (ii = 0; ii < lpBitsInfoOut->bmiHeader.biClrUsed; ++ii)
  1174. while (ii--)
  1175. SWAPTYPE(argbq[ii].rgbRed, argbq[ii].rgbBlue, BYTE);
  1176. // Write Palette Info
  1177. dw = sizeof(RGBQUAD) * lpBitsInfoOut->bmiHeader.biClrUsed;
  1178. if (mmioWrite(lpcs->hmmio, (LPBYTE)argbq, dw) != (long)dw)
  1179. goto FileError;
  1180. }
  1181. if (mmioAscend(lpcs->hmmio, &ck, 0))
  1182. goto FileError;
  1183. // ADD FOURCC stuff here!!! for Video stream
  1184. // Ascend out of stream header
  1185. if (mmioAscend(lpcs->hmmio, &ckStream, 0))
  1186. goto FileError;
  1187. /* If sound is enabled, then write WAVE header */
  1188. if (fSound) {
  1189. /* Create stream header list */
  1190. ckStream.cksize = 0;
  1191. ckStream.fccType = listtypeSTREAMHEADER;
  1192. if (mmioCreateChunk(lpcs->hmmio,&ckStream,MMIO_CREATELIST))
  1193. goto FileError;
  1194. ZeroMemory (&strhdr, sizeof(strhdr));
  1195. strhdr.fccType = streamtypeAUDIO;
  1196. strhdr.fccHandler = 0L;
  1197. strhdr.dwFlags = 0L;
  1198. #ifdef NEW_COMPMAN
  1199. strhdr.wPriority = 0L;
  1200. strhdr.wLanguage = 0L;
  1201. #else
  1202. strhdr.dwPriority = 0L;
  1203. #endif
  1204. strhdr.dwInitialFrames = 0L;
  1205. strhdr.dwScale = lpcs->lpWaveFormat->nBlockAlign;
  1206. strhdr.dwRate = lpcs->lpWaveFormat->nAvgBytesPerSec;
  1207. strhdr.dwStart = 0L;
  1208. strhdr.dwLength = lpcs->dwWaveBytes /
  1209. lpcs->lpWaveFormat->nBlockAlign;
  1210. strhdr.dwQuality = (DWORD)-1L; /* !!! ICQUALITY_DEFAULT */
  1211. strhdr.dwSampleSize = lpcs->lpWaveFormat->nBlockAlign;
  1212. ck.ckid = ckidSTREAMHEADER;
  1213. if (mmioCreateChunk(lpcs->hmmio,&ck,0) ||
  1214. mmioWrite(lpcs->hmmio, (LPBYTE)&strhdr, sizeof(strhdr)) != sizeof(strhdr) ||
  1215. mmioAscend(lpcs->hmmio, &ck, 0))
  1216. goto FileError;
  1217. ck.cksize = (LONG) GetSizeOfWaveFormat ((LPWAVEFORMATEX) lpcs->lpWaveFormat);
  1218. ck.ckid = ckidSTREAMFORMAT;
  1219. if (mmioCreateChunk(lpcs->hmmio,&ck,0) ||
  1220. mmioWrite(lpcs->hmmio, (LPBYTE)lpcs->lpWaveFormat, ck.cksize) != (LONG) ck.cksize ||
  1221. mmioAscend(lpcs->hmmio, &ck, 0))
  1222. goto FileError;
  1223. /* Ascend out of stream header */
  1224. if (mmioAscend(lpcs->hmmio, &ckStream, 0))
  1225. goto FileError;
  1226. }
  1227. // ADD FOURCC stuff here!!! for entire file
  1228. DSTATUS(lpcs, "Writing Info chunks");
  1229. if (lpcs->lpInfoChunks) {
  1230. DSTATUS(lpcs, "Writing Info chunks");
  1231. if (mmioWrite (lpcs->hmmio, lpcs->lpInfoChunks, lpcs->cbInfoChunks) !=
  1232. lpcs->cbInfoChunks)
  1233. goto FileError;
  1234. }
  1235. /* ascend from the Header list */
  1236. if (mmioAscend(lpcs->hmmio, &ckList, 0))
  1237. goto FileError;
  1238. ck.ckid = ckidAVIPADDING;
  1239. if (mmioCreateChunk(lpcs->hmmio,&ck,0))
  1240. goto FileError;
  1241. mmioSeek(lpcs->hmmio, lpcs->dwAVIHdrSize - 3 * sizeof(DWORD), SEEK_SET);
  1242. if (mmioAscend(lpcs->hmmio, &ck, 0))
  1243. goto FileError;
  1244. DSTATUS(lpcs, "Writing Movie LIST");
  1245. /* Start the movi list */
  1246. ckList.cksize = 0;
  1247. ckList.fccType = listtypeAVIMOVIE;
  1248. if (mmioCreateChunk(lpcs->hmmio,&ckList,MMIO_CREATELIST))
  1249. goto FileError;
  1250. // Force the chunk to end on the next word boundary
  1251. dprintf("IndexStartOffset = %8X\n", dwDataEnd);
  1252. mmioSeek(lpcs->hmmio, dwDataEnd + (dwDataEnd & 1L), SEEK_SET);
  1253. /* Ascend out of the movi list and the RIFF chunk so that */
  1254. /* the sizes can be fixed */
  1255. mmioAscend(lpcs->hmmio, &ckList, 0);
  1256. /*
  1257. ** Now write index out!
  1258. */
  1259. DSTATUS(lpcs, "Writing Index...");
  1260. WriteIndex(lpcs, fWroteJunkChunks);
  1261. lpcs->fFileCaptured = TRUE; // we got a good file, allow editing of it
  1262. goto Success;
  1263. FileError:
  1264. lpcs->fFileCaptured = fRet = FALSE; // bogus file - no editing allowed
  1265. Success:
  1266. DSTATUS(lpcs, "Freeing Index...");
  1267. FiniIndex (lpcs);
  1268. mmioAscend(lpcs->hmmio, &ckRiff, 0);
  1269. mmioSeek(lpcs->hmmio, 0, SEEK_END);
  1270. mmioFlush(lpcs->hmmio, 0);
  1271. // Close the file
  1272. mmioClose(lpcs->hmmio, 0);
  1273. lpcs->hmmio = NULL;
  1274. return fRet;
  1275. }
  1276. //
  1277. // Prepends dummy frame entries to the current valid video frame.
  1278. // Bumps the index, but does not actually trigger a write operation.
  1279. // nCount is a count of the number of frames to write
  1280. // Returns: TRUE on a successful write
  1281. BOOL WINAPI AVIWriteDummyFrames (
  1282. LPCAPSTREAM lpcs,
  1283. UINT nCount,
  1284. LPUINT lpuError,
  1285. LPBOOL lpbPending)
  1286. {
  1287. DWORD dwBytesToWrite;
  1288. DWORD dwType;
  1289. LPRIFF priff;
  1290. UINT jj;
  1291. *lpbPending = FALSE;
  1292. *lpuError = 0;
  1293. if ( ! nCount)
  1294. return TRUE;
  1295. // create a buffer full of dummy chunks to act as placeholders
  1296. // for the dropped frames
  1297. //
  1298. dwType = MAKEAVICKID(cktypeDIBbits, 0);
  1299. if (lpcs->lpBitsInfo->bmiHeader.biCompression == BI_RLE8)
  1300. dwType = MAKEAVICKID(cktypeDIBcompressed, 0);
  1301. // dont try to write more than 1 'sector' worth of dummy
  1302. // frames
  1303. //
  1304. dwBytesToWrite = nCount * sizeof(RIFF);
  1305. if (dwBytesToWrite > lpcs->dwBytesPerSector)
  1306. {
  1307. #ifdef DEBUG
  1308. UINT n = nCount;
  1309. #endif
  1310. dwBytesToWrite = lpcs->dwBytesPerSector;
  1311. #ifdef DEBUG
  1312. nCount = dwBytesToWrite / sizeof(RIFF);
  1313. assert(nCount*sizeof(RIFF) == dwBytesToWrite);
  1314. dprintf("Forced to reduce dummy frames from %d to %d", n, nCount);
  1315. #endif
  1316. }
  1317. // create index entries for the dummy chunks
  1318. //
  1319. for (jj = 0; jj < nCount-1; ++jj)
  1320. IndexVideo (lpcs, IS_DUMMY_CHUNK, FALSE);
  1321. IndexVideo (lpcs, IS_DUMMY_CHUNK | IS_GRANULAR_CHUNK, FALSE);
  1322. // fill in the drop frame buffer with dummy frames
  1323. //
  1324. priff = (LPRIFF)lpcs->lpDropFrame;
  1325. for (jj = 0; jj < nCount; ++jj, ++priff)
  1326. {
  1327. priff->dwSize = 0;
  1328. priff->dwType = dwType;
  1329. }
  1330. //
  1331. // cant use a single dummy frame buffer when we are doing async
  1332. // write because we cant write 'n' dummy frames to the buffer
  1333. // if it is currently already queued to an IO.
  1334. //
  1335. // perhaps several dummy frames? 1 frame, 2 frames, 3 frames, etc
  1336. // create dynamically?
  1337. //
  1338. // write out the dummy frames
  1339. //
  1340. AuxDebugEx (3, DEBUGLINE "DummyFrames Count=%d, ToWrite=%d\r\n",
  1341. nCount, dwBytesToWrite);
  1342. *lpuError = AVIWrite (lpcs,
  1343. lpcs->lpDropFrame,
  1344. dwBytesToWrite,
  1345. (UINT)-1, // force sync completion
  1346. ASYNC_BUF_DROP,
  1347. lpbPending);
  1348. return !(*lpuError);
  1349. }
  1350. // Writes compressed or uncompressed frames to the AVI file
  1351. // returns TRUE if no error, FALSE if end of file.
  1352. //
  1353. BOOL WINAPI AVIWriteVideoFrame (
  1354. LPCAPSTREAM lpcs,
  1355. LPBYTE lpData,
  1356. DWORD dwBytesUsed,
  1357. BOOL fKeyFrame,
  1358. UINT uIndex,
  1359. UINT nDropped,
  1360. LPUINT lpuError,
  1361. LPBOOL lpbPending)
  1362. {
  1363. DWORD dwBytesToWrite;
  1364. LPRIFF priff;
  1365. *lpuError = 0;
  1366. *lpbPending = FALSE;
  1367. if (!IndexVideo (lpcs,
  1368. dwBytesUsed | (nDropped ? 0 : IS_GRANULAR_CHUNK),
  1369. fKeyFrame))
  1370. return FALSE;
  1371. // adjust the size field of the RIFF chunk that preceeds the
  1372. // data to be written
  1373. //
  1374. priff = ((LPRIFF)lpData)-1;
  1375. priff->dwSize = dwBytesUsed;
  1376. dwBytesUsed += dwBytesUsed & 1;
  1377. dwBytesToWrite = dwBytesUsed + sizeof(RIFF);
  1378. if (nDropped)
  1379. {
  1380. UINT jj;
  1381. DWORD dwType;
  1382. // determine the 'type' of the dummy chunks
  1383. //
  1384. //dwType = priff->dwType;
  1385. dwType = MAKEAVICKID(cktypeDIBbits, 0);
  1386. if (lpcs->lpBitsInfo->bmiHeader.biCompression == BI_RLE8)
  1387. dwType = MAKEAVICKID(cktypeDIBcompressed, 0);
  1388. // dont try to write more than 1 'sector' worth of dummy
  1389. // frames
  1390. //
  1391. if (nDropped > (lpcs->dwBytesPerSector / sizeof(RIFF)))
  1392. nDropped = lpcs->dwBytesPerSector / sizeof(RIFF);
  1393. // create index entries for the dummy chunks
  1394. //
  1395. for (jj = 0; jj < nDropped-1; ++jj)
  1396. IndexVideo (lpcs, IS_DUMMY_CHUNK, FALSE);
  1397. IndexVideo (lpcs, IS_DUMMY_CHUNK | IS_GRANULAR_CHUNK, FALSE);
  1398. // fill in the drop frame buffer with dummy frames
  1399. //
  1400. priff = (LPRIFF)(lpData + dwBytesToWrite - sizeof(RIFF));
  1401. for (jj = 0; jj < nDropped; ++jj, ++priff)
  1402. {
  1403. priff->dwSize = 0;
  1404. priff->dwType = dwType;
  1405. }
  1406. dwBytesToWrite += nDropped * sizeof(RIFF);
  1407. }
  1408. // AviWrite will write the data and create any trailing junk
  1409. // that is necessary
  1410. //
  1411. // write out the chunk, video data, and possibly the junk chunk
  1412. //
  1413. AuxDebugEx (3, DEBUGLINE "Calling AVIWrite - Video=%8x dw=%8x\r\n",
  1414. (LPBYTE)lpData - sizeof(RIFF), dwBytesToWrite);
  1415. *lpuError = AVIWrite (lpcs,
  1416. (LPBYTE)lpData - sizeof(RIFF),
  1417. dwBytesToWrite,
  1418. uIndex,
  1419. ASYNC_BUF_VIDEO,
  1420. lpbPending);
  1421. return !(*lpuError);
  1422. }
  1423. // New for Chicago, align audio buffers on wChunkGranularity boundaries!
  1424. //
  1425. BOOL WINAPI AVIWriteAudio (
  1426. LPCAPSTREAM lpcs,
  1427. LPWAVEHDR lpwh,
  1428. UINT uIndex,
  1429. LPUINT lpuError,
  1430. LPBOOL lpbPending)
  1431. {
  1432. DWORD dwBytesToWrite;
  1433. LPRIFF priff;
  1434. *lpuError = 0;
  1435. *lpbPending = FALSE;
  1436. // change the dwSize field in the RIFF chunk
  1437. priff = ((LPRIFF)lpwh->lpData) -1;
  1438. priff->dwSize = lpwh->dwBytesRecorded;
  1439. if ( ! IndexAudio (lpcs, lpwh->dwBytesRecorded | IS_GRANULAR_CHUNK))
  1440. return FALSE;
  1441. // update total bytes of wave audio recorded
  1442. //
  1443. lpcs->dwWaveBytes += lpwh->dwBytesRecorded;
  1444. // pad the data to be written to a WORD (16 bit) boundary
  1445. //
  1446. lpwh->dwBytesRecorded += lpwh->dwBytesRecorded & 1;
  1447. dwBytesToWrite = lpwh->dwBytesRecorded + sizeof(RIFF);
  1448. // write out the chunk, audio data, and possibly the junk chunk
  1449. AuxDebugEx (3, DEBUGLINE "Audio=%8x dw=%8x\r\n",
  1450. lpwh->lpData - sizeof(RIFF), dwBytesToWrite);
  1451. *lpuError = AVIWrite (lpcs,
  1452. lpwh->lpData - sizeof(RIFF),
  1453. dwBytesToWrite,
  1454. uIndex,
  1455. ASYNC_BUF_AUDIO,
  1456. lpbPending);
  1457. return !(*lpuError);
  1458. }
  1459. #endif //---------------- USE_AVIFILE ----------------------------