Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

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