|
|
/****************************************************************************
* * capavi.c * * Main video capture module. * * Microsoft Video for Windows Sample Capture Class * * Copyright (c) 1992, 1993 Microsoft Corporation. All Rights Reserved. * * You have a royalty-free right to use, modify, reproduce and * distribute the Sample Files (and/or any modified version) in * any way you find useful, provided that you agree that * Microsoft has no warranty obligations or liability for any * Sample Application Files which are modified. * ***************************************************************************/
#include <windows.h>
#include <windowsx.h>
#include <mmsystem.h>
#include <memory.h> // for _fmemset
#include <msvideo.h>
#include <drawdib.h>
#include <mmreg.h>
#include <mmddk.h>
#include <msacm.h>
#include <avifmt.h>
#include "avicap.h"
#include "avicapi.h"
#include "time.h"
time_t ltime;
extern void NEAR PASCAL MemCopy(LPVOID, LPVOID, DWORD); // in memcopy.asm
extern WORD FAR PASCAL SmartDrv(char chDrive, WORD w); extern WORD GetSizeOfWaveFormat (LPWAVEFORMATEX lpwf);
/* dialog function prototype */ LONG FAR PASCAL _export capseqDlgProc(HWND hwnd, unsigned msg, WORD wParam, LONG lParam);
#ifdef _DEBUG
#define DSTATUS(lpcs, sz) statusUpdateStatus(lpcs, IDS_CAP_INFO, (LPSTR) sz)
#else
#define DSTATUS(lpcs, sz)
#endif
///////////////////////////////////////////////////////////////////////////
// The index array is used to record the positions
// of every chunk in the RIFF (avi) file.
//
// what this array is:
//
// each entry contains the size of the data
// high order bits encode the type of data (audio / video)
// and whether the video chunk is a key frame, dropped frame, etc.
///////////////////////////////////////////////////////////////////////////
// The following are anded with the size in the index
#define IS_AUDIO_CHUNK 0x80000000
#define IS_KEYFRAME_CHUNK 0x40000000
#define IS_DUMMY_CHUNK 0x20000000
#define IS_LAST_DUMMY_CHUNK 0x10000000
#define INDEX_MASK (IS_AUDIO_CHUNK | IS_KEYFRAME_CHUNK | IS_DUMMY_CHUNK | IS_LAST_DUMMY_CHUNK)
// Allocate the index table
// Returns: TRUE if index can be allocated
BOOL InitIndex (LPCAPSTREAM lpcs) { lpcs->dwIndex = 0;
WinAssert (lpcs->lpdwIndexStart == NULL);
// Limit index size between 1 minute at 30fps and 3 hours at 30fps
lpcs->sCapParms.dwIndexSize = max (lpcs->sCapParms.dwIndexSize, 1800); lpcs->sCapParms.dwIndexSize = min (lpcs->sCapParms.dwIndexSize, 324000L); dprintf("Max Index Size = %ld \n", lpcs->sCapParms.dwIndexSize);
if (lpcs->hIndex = GlobalAlloc (GMEM_MOVEABLE, lpcs->sCapParms.dwIndexSize * sizeof (DWORD))) { if (lpcs->lpdwIndexEntry = lpcs->lpdwIndexStart = (DWORD _huge *)GlobalLock (lpcs->hIndex)) { GlobalPageLock (lpcs->hIndex); return TRUE; // Success
} GlobalFree (lpcs->hIndex); } lpcs->hIndex = NULL; lpcs->lpdwIndexStart = NULL; return FALSE; }
// Deallocate the index table
void FiniIndex (LPCAPSTREAM lpcs) { if (lpcs->hIndex) { GlobalPageUnlock (lpcs->hIndex); if (lpcs->lpdwIndexStart) GlobalUnlock (lpcs->hIndex); GlobalFree (lpcs->hIndex); } lpcs->hIndex = NULL; lpcs->lpdwIndexStart = NULL; }
// Add an index entry for a video frame
// dwSize is the size of data ONLY, not including the chunk or junk
// Returns: TRUE if index space is not exhausted
BOOL IndexVideo (LPCAPSTREAM lpcs, DWORD dwSize, BOOL bKeyFrame) { BOOL fOK = lpcs->dwIndex < lpcs->sCapParms.dwIndexSize;
if (fOK) { *lpcs->lpdwIndexEntry++ = dwSize | (bKeyFrame ? IS_KEYFRAME_CHUNK : 0); lpcs->dwIndex++; lpcs->dwVideoChunkCount++; } return (fOK); }
// Add an index entry for an audio buffer
// dwSize is the size of data ONLY, not including the chunk or junk
// Returns: TRUE if index space is not exhausted
BOOL IndexAudio (LPCAPSTREAM lpcs, DWORD dwSize) { BOOL fOK = lpcs->dwIndex < lpcs->sCapParms.dwIndexSize;
if (fOK) { *lpcs->lpdwIndexEntry++ = dwSize | IS_AUDIO_CHUNK; lpcs->dwIndex++; lpcs->dwWaveChunkCount++; } return (fOK); }
// Write out the index at the end of the capture file.
// The single frame capture methods do not append
// JunkChunks! Audio chunks do not have junk appended.
BOOL WriteIndex (LPCAPSTREAM lpcs, BOOL fJunkChunkWritten) { BOOL fChunkIsAudio; BOOL fChunkIsKeyFrame; BOOL fChunkIsDummy; BOOL fChunkIsLastDummy; DWORD dwIndex; DWORD dw; DWORD dwDummySize; DWORD dwJunk; DWORD off; AVIINDEXENTRY avii; MMCKINFO ck; DWORD _huge *lpdw;
if (lpcs->dwIndex > lpcs->sCapParms.dwIndexSize) return TRUE;
off = lpcs->dwAVIHdrSize;
ck.cksize = 0; ck.ckid = ckidAVINEWINDEX; ck.fccType = 0;
if (mmioCreateChunk(lpcs->hmmio,&ck,0)) return FALSE;
lpdw = lpcs->lpdwIndexStart; for (dwIndex= 0; dwIndex< lpcs->dwIndex; dwIndex++) {
dw = *lpdw++;
fChunkIsAudio = (BOOL) ((dw & IS_AUDIO_CHUNK) != 0); fChunkIsKeyFrame = (BOOL) ((dw & IS_KEYFRAME_CHUNK) != 0); fChunkIsDummy = (BOOL) ((dw & IS_DUMMY_CHUNK) != 0); fChunkIsLastDummy = (BOOL) ((dw & IS_LAST_DUMMY_CHUNK) != 0); dw &= ~(INDEX_MASK);
if (fChunkIsAudio) { avii.ckid = MAKEAVICKID(cktypeWAVEbytes, 1); avii.dwFlags = 0; } else { if (lpcs->lpBitsInfo->bmiHeader.biCompression == BI_RLE8) avii.ckid = MAKEAVICKID(cktypeDIBcompressed, 0); else avii.ckid = MAKEAVICKID(cktypeDIBbits, 0); avii.dwFlags = fChunkIsKeyFrame ? AVIIF_KEYFRAME : 0; } avii.dwChunkLength = dw; avii.dwChunkOffset = off;
if (mmioWrite(lpcs->hmmio, (LPVOID)&avii, sizeof(avii)) != sizeof(avii)) return FALSE;
dw += sizeof (RIFF); off += dw;
// ooh, getting messy. We know that dummy chunks come in a group
// (1 or more) and are always terminated by a IS_LAST_DUMMY_CHUNK flag.
// only the last one gets junk append to round out to 2K
if (fChunkIsDummy) { dwDummySize += sizeof(RIFF); if (!fChunkIsLastDummy) continue; else dw = dwDummySize; // total size of all dummy entries in group
} else dwDummySize = 0;
if (fJunkChunkWritten & !fChunkIsAudio) { // If a Junk chunk was appended, move past it
if (dw % lpcs->sCapParms.wChunkGranularity) { dwJunk = lpcs->sCapParms.wChunkGranularity - (dw % lpcs->sCapParms.wChunkGranularity);
if (dwJunk < sizeof (RIFF)) off += lpcs->sCapParms.wChunkGranularity + dwJunk; else off += dwJunk; } }
if (off & 1) off++; }
if (mmioAscend(lpcs->hmmio, &ck, 0)) return FALSE;
return TRUE; }
// Allocate DOS memory for faster disk writes
LPVOID NEAR PASCAL AllocDosMem (DWORD dw) { HANDLE h;
if (h = LOWORD (GlobalDosAlloc(dw))) return (GlobalLock (h)); return NULL; }
// General purpose memory allocator
LPVOID NEAR PASCAL AllocMem (DWORD dw, BOOL fUseDOSMemory) { #if 0
if (fUseDOSMemory) return AllocDosMem(dw); #endif
return GlobalAllocPtr (GMEM_MOVEABLE, dw); }
void NEAR PASCAL FreeMem(LPVOID p) { GlobalFreePtr(p); }
#pragma optimize ("", off)
DWORD GetFreePhysicalMemory(void) { DWORD adw[ 0x30 / sizeof(DWORD) ]; WORD fFail;
//
// if standard mode just ask KERNEL how much memory is free
//
// if enhanced mode, call DPMI and find out how much *real*
// memory is free.
//
if (GetWinFlags() & WF_STANDARD) { return GetFreeSpace(0); } else _asm { mov ax, 0500h push ss pop es lea di, word ptr adw int 31h sbb ax, ax mov fFail, ax }
if (fFail) return (0l);
return (adw[2] * 4096); } #pragma optimize ("", on)
/*
* CalcWaveBufferSize - Figure out how large to make the wave buffers * a. At least .5 seconds * b. But not less than 10K, (else capture frmae rate suffers) * c. A multiple of lpcs->sCapParms.wChunkGranularity */ DWORD CalcWaveBufferSize (LPCAPSTREAM lpcs) { DWORD dw;
if (!lpcs-> lpWaveFormat) return 0L;
// at least .5 second
dw = (DWORD) lpcs->lpWaveFormat->nChannels * (DWORD) lpcs->lpWaveFormat->nSamplesPerSec * (lpcs->lpWaveFormat->wBitsPerSample / 8) / 2L; dw -= dw % lpcs->sCapParms.wChunkGranularity; dw = max ((1024L * 10), dw); // at least 10K
// dprintf("Wave buffer size = %ld \n", dw);
return dw; }
static BOOL IsWaveInDeviceMapped(HWAVEIN hWaveIn) { DWORD err; DWORD dw;
err = waveInMessage(hWaveIn, WIDM_MAPPER_STATUS, WAVEIN_MAPPER_STATUS_MAPPED, (DWORD)(LPVOID)&dw);
return err == 0 && dw != 0; }
// ****************************************************************
// ******************** Capture File Routines *********************
// ****************************************************************
/*
* AVIFileInit * * Perform all initialization required to write a capture file. * * We take a slightly strange approach: We don't write * out the header until we're done capturing. For now, * we just seek 2K into the file, which is where all of * the real data will go. * * When we're done, we'll come back and write out the header, * because then we'll know all of the values we need. * * Also allocate and init the index. */ BOOL AVIFileInit(LPCAPSTREAM lpcs) { #define TEMP_BUFF_SIZE 128
LONG l; char ach[TEMP_BUFF_SIZE]; LPBITMAPINFO lpBitsInfoOut; // Possibly compressed output format
/* No special video format given -- use the default */ if (lpcs->CompVars.hic == NULL) lpBitsInfoOut = lpcs->lpBitsInfo; else lpBitsInfoOut = lpcs->CompVars.lpbiOut;
WinAssert (lpcs->hmmio == NULL); // Should never have a file handle on entry
/* if the capture file has not been set then set it now */ if (!(*lpcs->achFile)){ // if (!fileSetCapFile())
goto INIT_FILE_OPEN_ERROR; }
/* we have a capture file, open it and set it up */ lpcs->hmmio = mmioOpen(lpcs->achFile, NULL, MMIO_WRITE); if (!lpcs->hmmio) { /* try and create */ lpcs->hmmio = mmioOpen(lpcs->achFile, NULL, MMIO_CREATE | MMIO_WRITE); if (!lpcs->hmmio) { goto INIT_FILE_OPEN_ERROR; } }
/* pre-read the file */ l = mmioSeek( lpcs->hmmio, 0L, SEEK_END ); while( l > 0 ) { l = mmioSeek( lpcs->hmmio, -min(l, 50000L), SEEK_CUR ); mmioRead( lpcs->hmmio, ach, sizeof(ach) ); }
/* Seek to 2K (or multiple of 2K), where we're going to write our data.
** later, we'll come back and fill in the file. */
// l is zero for standard wave and video formats
l = (GetSizeOfWaveFormat ((LPWAVEFORMATEX) lpcs->lpWaveFormat) - sizeof (PCMWAVEFORMAT)) + (lpBitsInfoOut->bmiHeader.biSize - sizeof (BITMAPINFOHEADER));
// (2K + size of wave and video stream headers) rounded to next 2K
lpcs->dwAVIHdrSize = AVI_HEADERSIZE + (((lpcs->cbInfoChunks + l + lpcs->sCapParms.wChunkGranularity - 1) / lpcs->sCapParms.wChunkGranularity) * lpcs->sCapParms.wChunkGranularity);
dprintf("AVIHdrSize = %ld \n", lpcs->dwAVIHdrSize); mmioSeek(lpcs->hmmio, lpcs->dwAVIHdrSize, SEEK_SET);
if (!InitIndex (lpcs)) // do all Index allocations
mmioClose (lpcs->hmmio, 0);
lpcs->dwVideoChunkCount = 0; lpcs->dwWaveChunkCount = 0;
INIT_FILE_OPEN_ERROR: return (lpcs->hmmio != NULL); }
/*
* AVIFileFini * * Write out the index, deallocate the index, and close the file. * */ BOOL AVIFileFini (LPCAPSTREAM lpcs, BOOL fWroteJunkChunks, BOOL fAbort) { MMCKINFO ckRiff; MMCKINFO ckList; MMCKINFO ckStream; MMCKINFO ck; int i; DWORD dw; AVIStreamHeader strhdr; DWORD dwDataEnd; BOOL fRet = TRUE; RGBQUAD argbq[256]; MainAVIHeader aviHdr; BOOL fSound = lpcs->sCapParms.fCaptureAudio; LPBITMAPINFO lpBitsInfoOut; // Possibly compressed output format
/* No special video format given -- use the default */ if (lpcs->CompVars.hic == NULL) lpBitsInfoOut = lpcs->lpBitsInfo; else lpBitsInfoOut = lpcs->CompVars.lpbiOut;
if (lpcs->hmmio == NULL) // This can be called even though never opened
return FALSE;
if (fAbort) goto FileError;
if (!lpcs->dwWaveBytes) fSound = FALSE;
dwDataEnd = mmioSeek(lpcs->hmmio, 0, SEEK_CUR);
/* Seek to beginning of file, so we can write the header. */ mmioSeek(lpcs->hmmio, 0, SEEK_SET);
DSTATUS(lpcs, "Writing AVI header");
/* Create RIFF chunk */ ckRiff.cksize = 0; ckRiff.fccType = formtypeAVI; if(mmioCreateChunk(lpcs->hmmio,&ckRiff,MMIO_CREATERIFF)) { goto FileError; }
/* Create header list */ ckList.cksize = 0; ckList.fccType = listtypeAVIHEADER; if(mmioCreateChunk(lpcs->hmmio,&ckList,MMIO_CREATELIST)) { goto FileError; }
/* Create AVI header chunk */ ck.cksize = sizeof(MainAVIHeader); ck.ckid = ckidAVIMAINHDR; if(mmioCreateChunk(lpcs->hmmio,&ck,0)) { goto FileError; }
lpcs->dwAVIHdrPos = ck.dwDataOffset;
/* Calculate AVI header info */ _fmemset(&aviHdr, 0, sizeof(aviHdr)); if (fSound && lpcs->dwVideoChunkCount) { /* HACK HACK */ /* Set rate that was captured based on length of audio data */
aviHdr.dwMicroSecPerFrame = (DWORD) ((double)lpcs->dwWaveBytes * 1000000. / ((double)lpcs->lpWaveFormat->nAvgBytesPerSec * lpcs->dwVideoChunkCount + 0.5)); } else { aviHdr.dwMicroSecPerFrame = lpcs->sCapParms.dwRequestMicroSecPerFrame; } lpcs->dwActualMicroSecPerFrame = aviHdr.dwMicroSecPerFrame;
aviHdr.dwMaxBytesPerSec = (DWORD) muldiv32 (lpBitsInfoOut->bmiHeader.biSizeImage, 1000000, lpcs->sCapParms.dwRequestMicroSecPerFrame) + (fSound ? lpcs->lpWaveFormat->nAvgBytesPerSec : 0); aviHdr.dwPaddingGranularity = 0L; aviHdr.dwFlags = AVIF_WASCAPTUREFILE | AVIF_HASINDEX; aviHdr.dwStreams = fSound ? 2 : 1; aviHdr.dwTotalFrames = lpcs->dwVideoChunkCount; aviHdr.dwInitialFrames = 0L; aviHdr.dwSuggestedBufferSize = 0L; aviHdr.dwWidth = lpBitsInfoOut->bmiHeader.biWidth; aviHdr.dwHeight = lpBitsInfoOut->bmiHeader.biHeight;
// The following were set for all versions before Chicago Beta2
// They are now listed as reserved...
// aviHdr.dwRate = 1000000L;
// aviHdr.dwScale = aviHdr.dwMicroSecPerFrame;
// aviHdr.dwStart = 0L;
// aviHdr.dwLength = lpcs->dwVideoChunkCount;
/* Write AVI header info */ if(mmioWrite(lpcs->hmmio, (LPSTR)&aviHdr, sizeof(aviHdr)) != sizeof(aviHdr)) { goto FileError; }
if(mmioAscend(lpcs->hmmio, &ck, 0)) { goto FileError; }
DSTATUS(lpcs, "Writing AVI Stream header");
/* Create stream header list */ ckStream.cksize = 0; ckStream.fccType = listtypeSTREAMHEADER; if(mmioCreateChunk(lpcs->hmmio,&ckStream,MMIO_CREATELIST)) { goto FileError; }
_fmemset(&strhdr, 0, sizeof(strhdr)); strhdr.fccType = streamtypeVIDEO; if (lpcs->CompVars.hic) strhdr.fccHandler = lpcs->CompVars.fccHandler; else strhdr.fccHandler = lpBitsInfoOut->bmiHeader.biCompression;
// A bit of history...
// In VFW 1.0, we set fccHandler to 0 for BI_RLE8 formats
// as a kludge to make Mplayer and Videdit play the files.
// Just prior to 1.1 release, we found this broke Premiere,
// so now (after AVICAP beta is on Compuserve), we change the
// fccHandler to "MRLE". Just ask Todd...
// And now, at RC1, we change it again to "RLE ", Just ask Todd...
if (strhdr.fccHandler == BI_RLE8) strhdr.fccHandler = mmioFOURCC('R', 'L', 'E', ' ');
strhdr.dwFlags = 0L; strhdr.wPriority = 0L; strhdr.wLanguage = 0L; strhdr.dwInitialFrames = 0L; strhdr.dwScale = aviHdr.dwMicroSecPerFrame; strhdr.dwRate = 1000000L; strhdr.dwStart = 0L; strhdr.dwLength = lpcs->dwVideoChunkCount; /* Needs to get filled in! */ strhdr.dwQuality = (DWORD) -1L; /* !!! ICQUALITY_DEFAULT */ strhdr.dwSampleSize = 0L;
ck.ckid = ckidSTREAMHEADER; if(mmioCreateChunk(lpcs->hmmio,&ck,0)) { goto FileError; }
/* Write stream header data */ if(mmioWrite(lpcs->hmmio, (LPSTR)&strhdr, sizeof(strhdr)) != sizeof(strhdr)) { goto FileError; }
if(mmioAscend(lpcs->hmmio, &ck, 0)) { goto FileError; }
/*
** !!! dont write palette for full color? */ if (lpBitsInfoOut->bmiHeader.biBitCount > 8) lpBitsInfoOut->bmiHeader.biClrUsed = 0;
/* Create DIB header chunk */ ck.cksize = lpBitsInfoOut->bmiHeader.biSize + lpBitsInfoOut->bmiHeader.biClrUsed * sizeof(RGBQUAD); ck.ckid = ckidSTREAMFORMAT; if(mmioCreateChunk(lpcs->hmmio,&ck,0)) { goto FileError; }
/* Write DIB header data */ if(mmioWrite(lpcs->hmmio, (LPSTR)&lpBitsInfoOut->bmiHeader, lpBitsInfoOut->bmiHeader.biSize) != (LONG) lpBitsInfoOut->bmiHeader.biSize) { goto FileError; }
if (lpBitsInfoOut->bmiHeader.biClrUsed > 0) { /* Get Palette info */ if(GetPaletteEntries(lpcs->hPalCurrent, 0, (WORD) lpBitsInfoOut->bmiHeader.biClrUsed, (LPPALETTEENTRY) argbq) != (WORD)lpBitsInfoOut->bmiHeader.biClrUsed) { goto FileError; }
for(i = 0; i < (int) lpBitsInfoOut->bmiHeader.biClrUsed; i++) SWAP(argbq[i].rgbRed, argbq[i].rgbBlue);
/* Write Palette Info */ dw = sizeof(RGBQUAD) * lpBitsInfoOut->bmiHeader.biClrUsed; if (mmioWrite(lpcs->hmmio, (LPSTR)argbq, dw) != (long)dw) { goto FileError; } }
if(mmioAscend(lpcs->hmmio, &ck, 0)) { goto FileError; }
// ADD FOURCC stuff here!!! for Video stream
/* Ascend out of stream header */ if(mmioAscend(lpcs->hmmio, &ckStream, 0)) { goto FileError; }
/* If sound is enabled, then write WAVE header */ if(fSound) {
/* Create stream header list */ ckStream.cksize = 0; ckStream.fccType = listtypeSTREAMHEADER; if(mmioCreateChunk(lpcs->hmmio,&ckStream,MMIO_CREATELIST)) { goto FileError; }
_fmemset(&strhdr, 0, sizeof(strhdr)); strhdr.fccType = streamtypeAUDIO; strhdr.fccHandler = 0L; strhdr.dwFlags = 0L; strhdr.wPriority = 0L; strhdr.wLanguage = 0L; strhdr.dwInitialFrames = 0L; strhdr.dwScale = lpcs->lpWaveFormat->nBlockAlign; strhdr.dwRate = lpcs->lpWaveFormat->nAvgBytesPerSec; strhdr.dwStart = 0L; strhdr.dwLength = lpcs->dwWaveBytes / lpcs->lpWaveFormat->nBlockAlign; strhdr.dwQuality = (DWORD)-1L; /* !!! ICQUALITY_DEFAULT */ strhdr.dwSampleSize = lpcs->lpWaveFormat->nBlockAlign;
ck.ckid = ckidSTREAMHEADER; if(mmioCreateChunk(lpcs->hmmio,&ck,0)) { goto FileError; }
if(mmioWrite(lpcs->hmmio, (LPSTR)&strhdr, sizeof(strhdr)) != sizeof(strhdr)) { goto FileError; }
if(mmioAscend(lpcs->hmmio, &ck, 0)) { goto FileError; }
ck.cksize = (LONG) GetSizeOfWaveFormat ((LPWAVEFORMATEX) lpcs->lpWaveFormat); ck.ckid = ckidSTREAMFORMAT; if(mmioCreateChunk(lpcs->hmmio,&ck,0)) { goto FileError; }
/* Write WAVE header info */ if(mmioWrite(lpcs->hmmio, (LPSTR)lpcs->lpWaveFormat, ck.cksize) != (LONG) ck.cksize) { goto FileError; }
if(mmioAscend(lpcs->hmmio, &ck, 0)) { goto FileError; }
/* Ascend out of stream header */ if(mmioAscend(lpcs->hmmio, &ckStream, 0)) { goto FileError; }
}
// ADD FOURCC stuff here!!! for entire file
DSTATUS(lpcs, "Writing Info chunks"); if (lpcs->lpInfoChunks) { DSTATUS(lpcs, "Writing Info chunks"); if (mmioWrite (lpcs->hmmio, lpcs->lpInfoChunks, lpcs->cbInfoChunks) != lpcs->cbInfoChunks) goto FileError; }
/* ascend from the Header list */ if(mmioAscend(lpcs->hmmio, &ckList, 0)) { goto FileError; }
ck.ckid = ckidAVIPADDING; if(mmioCreateChunk(lpcs->hmmio,&ck,0)) { goto FileError; }
mmioSeek(lpcs->hmmio, lpcs->dwAVIHdrSize - 3 * sizeof(DWORD), SEEK_SET);
if(mmioAscend(lpcs->hmmio, &ck, 0)) { goto FileError; }
DSTATUS(lpcs, "Writing Movie LIST");
/* Start the movi list */ ckList.cksize = 0; ckList.fccType = listtypeAVIMOVIE; if(mmioCreateChunk(lpcs->hmmio,&ckList,MMIO_CREATELIST)) { goto FileError; }
// Force the chunk to end on the next word boundary
mmioSeek(lpcs->hmmio, dwDataEnd + (dwDataEnd & 1L), SEEK_SET);
/* Ascend out of the movi list and the RIFF chunk so that */ /* the sizes can be fixed */ mmioAscend(lpcs->hmmio, &ckList, 0);
/*
** Now write index out! */ DSTATUS(lpcs, "Writing Index..."); WriteIndex(lpcs, fWroteJunkChunks);
lpcs->fFileCaptured = TRUE; // we got a good file, allow editing of it
goto Success;
FileError: lpcs->fFileCaptured = fRet = FALSE; // bogus file - no editing allowed
Success: DSTATUS(lpcs, "Freeing Index..."); FiniIndex (lpcs); mmioAscend(lpcs->hmmio, &ckRiff, 0);
mmioSeek(lpcs->hmmio, 0, SEEK_END);
mmioFlush(lpcs->hmmio, 0);
/* Close the file */ mmioClose(lpcs->hmmio, 0); lpcs->hmmio = NULL;
return fRet; }
// ****************************************************************
// ******************** Audio Buffer Control **********************
// ****************************************************************
// Audio buffers are always allocated under the presumption that
// audio capture may be enabled at any time.
// AVIAudioInit must be matched with AVIAudioFini (both only called once)
// AVIAudioPrepare must be matched with AVIAudioUnPrepare
// (which may be called multiple times to enable and disable audio)
// AVI AudioInit - Allocate and initialize buffers for audio capture.
// This routine is also used by MCI capture.
// Returns: 0 on success, otherwise an error code.
WORD AVIAudioInit (LPCAPSTREAM lpcs) { int i; LPVOID p;
if (lpcs->sCapParms.wNumAudioRequested == 0) lpcs->sCapParms.wNumAudioRequested = DEF_WAVE_BUFFERS;
// Alloc the wave memory
for(i = 0; i < (int)lpcs->sCapParms.wNumAudioRequested; i++) {
p = AllocMem(sizeof(WAVEHDR) + lpcs->dwWaveSize, FALSE /* DOSMem */);
if (p == NULL) break;
lpcs->alpWaveHdr[i] = p; lpcs->alpWaveHdr[i]->lpData = (LPBYTE)p + sizeof(WAVEHDR) + sizeof(RIFF); lpcs->alpWaveHdr[i]->dwBufferLength = lpcs->dwWaveSize - sizeof(RIFF); lpcs->alpWaveHdr[i]->dwBytesRecorded = 0; lpcs->alpWaveHdr[i]->dwUser = 0; lpcs->alpWaveHdr[i]->dwFlags = 0; lpcs->alpWaveHdr[i]->dwLoops = 0;
/* Set Chunk ID, Size in buffer */ p = (LPBYTE)p + sizeof(WAVEHDR);
((LPRIFF)p)->dwType = MAKEAVICKID(cktypeWAVEbytes, 1); ((LPRIFF)p)->dwSize = lpcs->dwWaveSize - sizeof(RIFF); }
lpcs->iNumAudio = i;
return ((lpcs->iNumAudio == 0) ? IDS_CAP_WAVE_ALLOC_ERROR : 0); }
//
// AVI AudioFini - UnPrepares headers and resets the wave device.
// This routine is also used by MCI capture.
// Returns: 0 on success, otherwise an error code.
WORD AVIAudioFini (LPCAPSTREAM lpcs) { int i;
/* free headers and data */ for(i=0; i < MAX_WAVE_BUFFERS; i++) { if (lpcs->alpWaveHdr[i]) { FreeMem(lpcs->alpWaveHdr[i]); lpcs->alpWaveHdr[i] = NULL; } }
return 0; }
//
// AVI AudioPrepare - Opens the wave device and adds the buffers
// Prepares headers and adds buffers to the device
// This routine is also used by MCI capture.
// Returns: 0 on success, otherwise an error code.
WORD AVIAudioPrepare (LPCAPSTREAM lpcs, HWND hWndCallback) { UINT uiError; int i;
/* See if we can open that format for input */
uiError = waveInOpen((LPHWAVEIN)&lpcs->hWaveIn, (UINT)WAVE_MAPPER, lpcs->lpWaveFormat, (DWORD) hWndCallback, 0L, (hWndCallback ? CALLBACK_WINDOW : 0L));
if (uiError != MMSYSERR_NOERROR) return IDS_CAP_WAVE_OPEN_ERROR;
lpcs->fAudioYield = IsWaveInDeviceMapped(lpcs->hWaveIn); lpcs->fAudioBreak = FALSE; DPF("AVICap: AudioYield = %d \n", lpcs->fAudioYield);
for(i = 0; i < (int)lpcs->sCapParms.wNumAudioRequested; i++) { if (waveInPrepareHeader(lpcs->hWaveIn, lpcs->alpWaveHdr[i], sizeof(WAVEHDR))) return IDS_CAP_WAVE_ALLOC_ERROR;
if (waveInAddBuffer(lpcs->hWaveIn, lpcs->alpWaveHdr[i], sizeof(WAVEHDR))) return IDS_CAP_WAVE_ALLOC_ERROR; }
lpcs->iNextWave = 0; // current wave
lpcs->dwWaveBytes = 0L; // number of wave bytes
lpcs->dwWaveChunkCount = 0; // number of wave frames
return 0; }
//
// AVI AudioUnPrepare - UnPrepares headers and closes the wave device.
// This routine is also used by MCI capture.
// Returns: 0 on success, otherwise an error code.
WORD AVIAudioUnPrepare (LPCAPSTREAM lpcs) { int i;
if (lpcs->hWaveIn) { waveInReset(lpcs->hWaveIn);
/* unprepare headers by unlocking them */ for(i=0; i < lpcs->iNumAudio; i++) { if (lpcs->alpWaveHdr[i]) { if (lpcs->alpWaveHdr[i]->dwFlags & WHDR_PREPARED) waveInUnprepareHeader(lpcs->hWaveIn, lpcs->alpWaveHdr[i], sizeof(WAVEHDR)); } }
waveInClose(lpcs->hWaveIn); lpcs->hWaveIn = NULL; } return 0; }
// ****************************************************************
// ******************** Video Buffer Control **********************
// ****************************************************************
// AVIVideoInit - Allocates, and initialize buffers for video capture.
// This routine is also used by MCI capture.
// Returns: 0 on success, otherwise an error code.
WORD AVIVideoInit (LPCAPSTREAM lpcs) { int iMaxVideo; DWORD dwFreeMem; DWORD dwUserRequests; DWORD dwAudioMem; int i; LPVOID p;
lpcs->iNextVideo = 0; lpcs->dwVideoChunkCount = 0; lpcs->dwFramesDropped = 0;
// When performing MCI step capture, buffer array is not used
if (lpcs->sCapParms.fStepMCIDevice) return 0;
// If the user hasn't specified the number of video buffers to use,
// assume the minimum
if (lpcs->sCapParms.wNumVideoRequested == 0) lpcs->sCapParms.wNumVideoRequested = MIN_VIDEO_BUFFERS;
iMaxVideo = min (MAX_VIDEO_BUFFERS, lpcs->sCapParms.wNumVideoRequested);
// Post VFW 1.1a, see if the driver can allocate memory
if (videoStreamAllocHdrAndBuffer (lpcs->hVideoIn, (LPVIDEOHDR FAR *) &p, (DWORD) sizeof(VIDEOHDR) + lpcs->dwVideoSize) == DV_ERR_OK) { lpcs-> fBuffersOnHardware = TRUE; videoStreamFreeHdrAndBuffer (lpcs->hVideoIn, (LPVIDEOHDR) p); } else { lpcs-> fBuffersOnHardware = FALSE;
// How much actual free physical memory exists?
dwFreeMem = GetFreePhysicalMemory();
dwAudioMem = lpcs->dwWaveSize * lpcs->sCapParms.wNumAudioRequested;
#define FOREVER_FREE 32768L // Always keep this free for swap space
// How much memory will be used if we allocate per the request?
dwUserRequests = dwAudioMem + lpcs->dwVideoSize * iMaxVideo + FOREVER_FREE;
// If request is greater than available memory, force fewer buffers
if (dwUserRequests > dwFreeMem) { if (dwFreeMem > dwAudioMem) dwFreeMem -= dwAudioMem; iMaxVideo = (int)(((dwFreeMem * 8) / 10) / lpcs->dwVideoSize); iMaxVideo = min (MAX_VIDEO_BUFFERS, iMaxVideo); dprintf("iMaxVideo = %d\n", iMaxVideo); } } // endif not allocating buffers from hardware
// Set up the buffers presuming fixed size DIBs and Junk chunks
// These will be modified later if the device provides compressed data
for (i=0; i < iMaxVideo; i++) {
if (lpcs-> fBuffersOnHardware) videoStreamAllocHdrAndBuffer (lpcs->hVideoIn, (LPVIDEOHDR FAR *) &p, sizeof(VIDEOHDR) + lpcs->dwVideoSize); else p = AllocMem(sizeof(VIDEOHDR) + lpcs->dwVideoSize, lpcs->sCapParms.fUsingDOSMemory /* DOSMem */);
if (p == NULL) break;
lpcs->alpVideoHdr[i] = p; lpcs->alpVideoHdr[i]->lpData = (LPBYTE)p + sizeof(VIDEOHDR) + sizeof(RIFF); lpcs->alpVideoHdr[i]->dwBufferLength = lpcs->lpBitsInfo->bmiHeader.biSizeImage; lpcs->alpVideoHdr[i]->dwBytesUsed = 0; lpcs->alpVideoHdr[i]->dwTimeCaptured = 0; lpcs->alpVideoHdr[i]->dwUser = 0; // Buffers on hardware are marked prepared during allocation!
if (!lpcs-> fBuffersOnHardware) lpcs->alpVideoHdr[i]->dwFlags = 0;
p = (LPBYTE)p + sizeof(VIDEOHDR);
if (lpcs->lpBitsInfo->bmiHeader.biCompression == BI_RLE8) ((LPRIFF)p)->dwType = MAKEAVICKID(cktypeDIBcompressed, 0); else ((LPRIFF)p)->dwType = MAKEAVICKID(cktypeDIBbits, 0); ((LPRIFF)p)->dwSize = lpcs->lpBitsInfo->bmiHeader.biSizeImage;
if(lpcs->dwVideoJunkSize) { p = ((BYTE huge *)p) + ((LPRIFF)p)->dwSize + sizeof(RIFF);
((LPRIFF)p)->dwType = ckidAVIPADDING;; ((LPRIFF)p)->dwSize = lpcs->dwVideoJunkSize; } }
lpcs->iNumVideo = i;
if (lpcs-> fBuffersOnHardware) dprintf("HARDWARE iNumVideo Allocated = %d \n", lpcs->iNumVideo); else if (lpcs->sCapParms.fUsingDOSMemory) dprintf("DOS iNumVideo Allocated = %d \n", lpcs->iNumVideo); else dprintf("HIGH iNumVideo Allocated = %d \n", lpcs->iNumVideo);
return ((lpcs->iNumVideo == 0) ? IDS_CAP_VIDEO_ALLOC_ERROR : 0); }
//
// AVIVideoPrepare - Prepares headers and adds buffers to the device
// This routine is also used by MCI capture.
// Returns: 0 on success, otherwise an error code.
WORD AVIVideoPrepare (LPCAPSTREAM lpcs) { int i;
// When performing MCI step capture, buffer array is not used
if (lpcs->sCapParms.fStepMCIDevice) return 0;
// Open the video stream, setting the capture rate
if (videoStreamInit(lpcs->hVideoIn, lpcs->sCapParms.dwRequestMicroSecPerFrame, 0L, 0L, 0L )) { dprintf("cant open video device!\n"); return IDS_CAP_VIDEO_OPEN_ERROR; }
// Prepare (lock) the buffers, and give them to the device
for (i=0; i < lpcs->iNumVideo; i++) { // If the buffers are on the hardware, don't Prepare them
if (!lpcs-> fBuffersOnHardware) { if (videoStreamPrepareHeader (lpcs->hVideoIn, lpcs->alpVideoHdr[i], sizeof(VIDEOHDR))) { lpcs->iNumVideo = i; dprintf("**** could only prepare %d Video!\n", lpcs->iNumVideo); break; } }
if (videoStreamAddBuffer(lpcs->hVideoIn, lpcs->alpVideoHdr[i], sizeof(VIDEOHDR))) return IDS_CAP_VIDEO_ALLOC_ERROR; } return 0; }
//
// AVI VideoUnPrepare - UnPrepares headers, frees memory, and
// resets the video in device.
// This routine is also used by MCI capture.
// Returns: 0 on success, otherwise an error code.
WORD AVIVideoUnPrepare (LPCAPSTREAM lpcs) { int i;
// When performing MCI step capture, buffer array is not used
if (lpcs->sCapParms.fStepMCIDevice) return 0;
/* Reset the buffers so they can be freed */ if (lpcs->hVideoIn) { videoStreamReset(lpcs->hVideoIn);
/* unprepare headers */ /* Unlock and free headers and data */
for(i = 0; i < MAX_VIDEO_BUFFERS; i++) { if (lpcs->alpVideoHdr[i]) { if (!lpcs-> fBuffersOnHardware) { if (lpcs->alpVideoHdr[i]->dwFlags & VHDR_PREPARED) videoStreamUnprepareHeader(lpcs->hVideoIn, lpcs->alpVideoHdr[i],sizeof(VIDEOHDR));
FreeMem(lpcs->alpVideoHdr[i]); } else videoStreamFreeHdrAndBuffer(lpcs->hVideoIn, lpcs->alpVideoHdr[i]); lpcs->alpVideoHdr[i] = NULL; } } // Shut down the video stream
videoStreamFini(lpcs->hVideoIn); } return 0; }
/*
* AVI Fini - undo the mess that AVIInit did. * */ void AVIFini(LPCAPSTREAM lpcs) { if (lpcs->lpDOSWriteBuffer) { FreeMem(lpcs->lpDOSWriteBuffer); lpcs->lpDOSWriteBuffer = NULL; }
AVIVideoUnPrepare (lpcs); // Free the video device and buffers
AVIAudioUnPrepare (lpcs); // Free the audio device
AVIAudioFini (lpcs); // Free the audio buffers
}
//
// AVI Init
// This routine does all the non-File initalization for AVICapture.
// Returns: 0 on success, Error string value on failure.
//
WORD AVIInit (LPCAPSTREAM lpcs) { WORD wError = 0; // Success
int i; LPBITMAPINFO lpBitsInfoOut; // Possibly compressed output format
/* No special video format given -- use the default */ if (lpcs->CompVars.hic == NULL) lpBitsInfoOut = lpcs->lpBitsInfo; else lpBitsInfoOut = lpcs->CompVars.lpbiOut;
// -------------------------------------------------------
// figure out buffer sizes
// -------------------------------------------------------
// Init all pointers to NULL
for(i = 0; i < MAX_VIDEO_BUFFERS; i++) lpcs->alpVideoHdr[i] = NULL;
for(i = 0; i < MAX_WAVE_BUFFERS; i++) lpcs->alpWaveHdr[i] = NULL;
// .5 second of audio per buffer (or 10K, whichever is larger)
if (lpcs->sCapParms.dwAudioBufferSize == 0) lpcs->dwWaveSize = CalcWaveBufferSize (lpcs); else { if (!lpcs-> lpWaveFormat) lpcs->dwWaveSize = 0; else lpcs->dwWaveSize = lpcs->sCapParms.dwAudioBufferSize; } /* Set video buffer size to Image size
(normally dx * dy * (depth / 8)) + sizeof(RIFF) */ lpcs->dwVideoSize = lpcs->lpBitsInfo->bmiHeader.biSizeImage + sizeof(RIFF); lpcs->fVideoDataIsCompressed = (lpBitsInfoOut->bmiHeader.biCompression != BI_RGB);
/* Pad out to multiple of lpcs->sCapParms.wChunkGranularity (2K) size */ // Calc dwVideoJunkSize
if (lpcs->dwVideoJunkSize = lpcs->sCapParms.wChunkGranularity - (lpcs->dwVideoSize % lpcs->sCapParms.wChunkGranularity)) { if (lpcs->dwVideoJunkSize < sizeof(RIFF)) lpcs->dwVideoJunkSize += lpcs->sCapParms.wChunkGranularity;
lpcs->dwVideoSize += lpcs->dwVideoJunkSize;
lpcs->dwVideoJunkSize -= sizeof(RIFF); } else { lpcs->dwVideoJunkSize = 0L; }
// -------------------------------------------------------
// DOS copy buffer
// -------------------------------------------------------
lpcs->dwDOSBufferSize = max (lpcs->dwWaveSize, lpcs->dwVideoSize);
#if 0
// Only get a DOS copy buffer if we're not trying to get DOS video buffers
if (!lpcs->sCapParms.fUsingDOSMemory) { lpcs->lpDOSWriteBuffer = AllocDosMem(lpcs->dwDOSBufferSize);
if (lpcs->lpDOSWriteBuffer) { dprintf("Allocated DOS write buffer (%ld bytes).\n", lpcs->dwDOSBufferSize); } else { dprintf("Unable to allocate DOS write buffer.\n"); } } #endif
// -------------------------------------------------------
// Init Sound
// -------------------------------------------------------
if (lpcs->sCapParms.fCaptureAudio) { if (wError = AVIAudioInit (lpcs)) { dprintf("can't init audio buffers!\n"); goto AVIInitFailed; } }
// -------------------------------------------------------
// Init Video
// -------------------------------------------------------
if (wError = AVIVideoInit (lpcs)) { dprintf("AVIVideoInitFailed (no buffers alloc'd)!\n"); goto AVIInitFailed; }
// --------------------------------------------------------------
// Prepare audio buffers (lock em down) and give them to the device
// --------------------------------------------------------------
if (lpcs->sCapParms.fCaptureAudio) { if (wError = AVIAudioPrepare (lpcs, NULL)) { dprintf("can't prepare audio buffers!\n"); goto AVIInitFailed; } }
// --------------------------------------------------------------
// Prepare video buffers (lock em down) and give them to the device
// --------------------------------------------------------------
if (wError = AVIVideoPrepare (lpcs)) { dprintf("can't prepare video buffers!\n"); goto AVIInitFailed; }
// -------------------------------------------------------
// all done, return success
// -------------------------------------------------------
return (0); // SUCCESS !
// -------------------------------------------------------
// we got a error, return string ID of error message
// -------------------------------------------------------
AVIInitFailed: AVIFini(lpcs); // Shutdown everything
return wError; }
// Write data to the capture file
// Returns: TRUE on a successful write
BOOL NEAR PASCAL AVIWrite(LPCAPSTREAM lpcs, LPVOID p, DWORD dwSize) { if (lpcs->lpDOSWriteBuffer) { MemCopy(lpcs->lpDOSWriteBuffer, p, dwSize); p = lpcs->lpDOSWriteBuffer; }
return mmioWrite(lpcs->hmmio, p, (long)dwSize) == (long)dwSize; }
//
// Writes dummy frames which on playback just repeat the previous frame
// nCount is a count of the number of frames to write
// Returns: TRUE on a successful write
BOOL AVIWriteDummyFrames (LPCAPSTREAM lpcs, int nCount) { DWORD dwBytesToWrite; DWORD dwJunkSize; LPRIFF p; int j;
p = (LPRIFF) lpcs->DropFrame; for (j = 0; j < nCount; j++) { // The index includes info on if this is a dummy chunk,
// AND if this is the last dummy chunk in a sequence
IndexVideo (lpcs, IS_DUMMY_CHUNK | ((j == nCount - 1) ? IS_LAST_DUMMY_CHUNK : 0), FALSE); if (lpcs->lpBitsInfo->bmiHeader.biCompression == BI_RLE8) p->dwType = MAKEAVICKID(cktypeDIBcompressed, 0); else p->dwType = MAKEAVICKID(cktypeDIBbits, 0); p->dwSize = 0; p++; }
dwBytesToWrite = nCount * sizeof(RIFF);
/* Pad out to multiple of lpcs->sCapParms.wChunkGranularity (2K) size */
if (dwJunkSize = (dwBytesToWrite % lpcs->sCapParms.wChunkGranularity)) { dwJunkSize = lpcs->sCapParms.wChunkGranularity - dwJunkSize; if (dwJunkSize < sizeof(RIFF)) dwJunkSize += lpcs->sCapParms.wChunkGranularity;
dwBytesToWrite += dwJunkSize;
dwJunkSize -= sizeof(RIFF); } else { dwJunkSize = 0L; }
// Now create a new junk chunk at the end of the compressed data
if(dwJunkSize) { p->dwType = ckidAVIPADDING; p->dwSize = dwJunkSize; }
/* write out the dummy frames, and possibly the junk chunk */ return (AVIWrite(lpcs, lpcs->DropFrame, dwBytesToWrite)); }
// Writes compressed or uncompressed frames to the AVI file
// returns TRUE if no error, FALSE if end of file.
BOOL AVIWriteVideoFrame (LPCAPSTREAM lpcs, LPVIDEOHDR lpVidHdr) { DWORD dwBytesToWrite; DWORD dwJunkSize; LPVOID p; LPVOID lpData;
// If the device compresses the data, calculate new junk chunk
// and fix the RIFF header
//
// We are automatically compressing during capture, so
// first compress the frame.
//
if (lpcs->CompVars.hic) { DWORD dwBytesUsed = 0; // don't force a data rate
BOOL fKeyFrame;
lpData = ICSeqCompressFrame(&lpcs->CompVars, 0, lpVidHdr->lpData, &fKeyFrame, &dwBytesUsed);
((RIFF FAR*)lpData)[-1].dwType = MAKEAVICKID(cktypeDIBbits, 0); ((RIFF FAR*)lpData)[-1].dwSize = dwBytesUsed;
if (fKeyFrame) lpVidHdr->dwFlags |= VHDR_KEYFRAME; else lpVidHdr->dwFlags &= ~VHDR_KEYFRAME;
lpVidHdr->dwBytesUsed = dwBytesUsed; } else { lpData = lpVidHdr->lpData; }
if (lpcs->fVideoDataIsCompressed) { // ie. if not BI_RGB
// change the dwSize field in the RIFF chunk
*((LPDWORD)((BYTE _huge *)lpVidHdr->lpData - sizeof(DWORD))) = lpVidHdr->dwBytesUsed;
// Make sure that the JUNK chunk starts on a WORD boundary
if (lpVidHdr->dwBytesUsed & 1) ++lpVidHdr->dwBytesUsed;
dwBytesToWrite = lpVidHdr->dwBytesUsed + sizeof(RIFF);
/* Pad out to multiple of lpcs->sCapParms.wChunkGranularity (2K) size */
if (dwJunkSize = (dwBytesToWrite % lpcs->sCapParms.wChunkGranularity)) { dwJunkSize = lpcs->sCapParms.wChunkGranularity - dwJunkSize; if (dwJunkSize < sizeof(RIFF)) dwJunkSize += lpcs->sCapParms.wChunkGranularity;
dwBytesToWrite += dwJunkSize;
// Now create a new junk chunk at the end of the compressed data
p = (BYTE huge *)lpVidHdr->lpData + lpVidHdr->dwBytesUsed;
((LPRIFF)p)->dwType = ckidAVIPADDING; ((LPRIFF)p)->dwSize = dwJunkSize - sizeof(RIFF); } } // endif compressed data
else { dwBytesToWrite = lpcs->dwVideoSize; } // endif not compressed data
/* write out the chunk, video data, and possibly the junk chunk */ return (AVIWrite(lpcs, (LPBYTE)lpData - sizeof(RIFF), dwBytesToWrite)); }
//
// Maintains info chunks which are written to the AVI header
//
BOOL FAR PASCAL SetInfoChunk(LPCAPSTREAM lpcs, LPCAPINFOCHUNK lpcic) { DWORD ckid = lpcic->fccInfoID; LPVOID lpData = lpcic->lpData; LONG cbData = lpcic->cbData; LPBYTE lp; LPBYTE lpw; LPBYTE lpEnd; LPBYTE lpNext; LONG cbSizeThis; BOOL fOK = FALSE;
// Delete all info chunks?
if (ckid == 0) { if (lpcs->lpInfoChunks) { GlobalFreePtr (lpcs->lpInfoChunks); lpcs->lpInfoChunks = NULL; lpcs->cbInfoChunks = 0; } return TRUE; }
// Try removing an entry if it already exists...
// Also used if lpData is NULL to just remove an entry
lpw = (LPBYTE)lpcs->lpInfoChunks; // always points at fcc
lpEnd = (LPBYTE)lpcs->lpInfoChunks + lpcs->cbInfoChunks; while (lpw < lpEnd) { cbSizeThis = ((LPDWORD)lpw)[1]; cbSizeThis += cbSizeThis & 1; // force WORD alignment
lpNext = lpw + cbSizeThis + sizeof (DWORD) * 2; if ((*(LPDWORD) lpw) == ckid) { lpcs->cbInfoChunks -= cbSizeThis + sizeof (DWORD) * 2; if (lpNext <= lpEnd) { if (lpEnd - lpNext) hmemcpy(lpw, lpNext, lpEnd - lpNext); if (lpcs->cbInfoChunks) { lpcs->lpInfoChunks = (LPBYTE) GlobalReAllocPtr( // shrink it
lpcs->lpInfoChunks, lpcs->cbInfoChunks, GMEM_MOVEABLE); } else { if (lpcs->lpInfoChunks) GlobalFreePtr (lpcs->lpInfoChunks); lpcs->lpInfoChunks = NULL; } fOK = TRUE; } break; } else lpw = lpNext; }
if (lpData == NULL || cbData == 0) // Only deleting, get out
return fOK;
// Add a new entry
cbData += cbData & 1; // force WORD alignment
cbData += sizeof(DWORD) * 2; // add sizeof 2 FOURCCs
if (lpcs->lpInfoChunks) { lp = (LPBYTE) GlobalReAllocPtr(lpcs->lpInfoChunks, lpcs->cbInfoChunks + cbData, GMEM_MOVEABLE); } else { lp = (LPBYTE) GlobalAllocPtr(GMEM_MOVEABLE, cbData); }
if (!lp) return FALSE;
// build RIFF chunk in block
((DWORD FAR *) (lp + lpcs->cbInfoChunks))[0] = ckid; ((DWORD FAR *) (lp + lpcs->cbInfoChunks))[1] = lpcic->cbData;
hmemcpy(lp + lpcs->cbInfoChunks + sizeof(DWORD) * 2, lpData, cbData - sizeof(DWORD) * 2); lpcs->lpInfoChunks = lp; lpcs->cbInfoChunks += cbData;
return TRUE; }
/*
* AVI Capture * This is the main streaming capture loop for both audio and * video. It will first init all buffers and drivers and then go into a * loop checking for buffers to be filled. When a buffer is filled then * the data for it is written out. * Afterwards it cleans up after itself (frees buffers etc...) * Returns: 0 on success, else error code */ void FAR PASCAL _loadds AVICapture1(LPCAPSTREAM lpcs) { BOOL fOK = TRUE; BOOL fT; BOOL fVideoBuffersInDOSMem; BOOL fStopping; // True when finishing capture
BOOL fStopped; // True if driver notified to stop
DWORD dw; char ach[128]; char achMsg[128]; WORD w; WORD wError; // Error String ID
DWORD dwDriverDropCount; WORD wSmartDrv; LPVIDEOHDR lpVidHdr; LPWAVEHDR lpWaveHdr; DWORD dwTimeStarted; // When did we start in milliseconds
DWORD dwTimeStopped; DWORD dwTimeToStop; // Lesser of MCI capture time or frame limit
BOOL fTryToPaint = FALSE; HDC hdc; HPALETTE hpalT; HCURSOR hOldCursor; RECT rcDrawRect; DWORD dwStreamError; CAPINFOCHUNK cic;
lpcs-> dwReturn = DV_ERR_OK;
hOldCursor = SetCursor(lpcs->hWaitCursor);
statusUpdateStatus(lpcs, IDS_CAP_BEGIN); // Always the first message
// If not 1 Meg. free, give it up!!!
if (GetFreePhysicalMemory () < (1024L * 1024L)) { errorUpdateError (lpcs, IDS_CAP_OUTOFMEM); lpcs-> dwReturn = IDS_CAP_OUTOFMEM; goto EarlyExit; }
statusUpdateStatus(lpcs, IDS_CAP_STAT_CAP_INIT);
// Try painting the DIB only if Live window
fTryToPaint = lpcs->fLiveWindow;
if (fTryToPaint) { hdc = GetDC(lpcs->hwnd); SetWindowOrg(hdc, lpcs->ptScroll.x, lpcs->ptScroll.y); hpalT = DrawDibGetPalette (lpcs->hdd); if (hpalT) hpalT = SelectPalette( hdc, hpalT, FALSE); RealizePalette(hdc); if (lpcs-> fScale) GetClientRect (lpcs->hwnd, &rcDrawRect); else SetRect (&rcDrawRect, 0, 0, lpcs->dxBits, lpcs->dyBits); }
// -------------------------------------------------------
// When should capture stop?
// -------------------------------------------------------
// If using MCI, capture for the shorter of the MCI period,
// or the capture limit
if (lpcs->sCapParms.fLimitEnabled) dwTimeToStop = (DWORD) ((DWORD) 1000 * lpcs->sCapParms.wTimeLimit); else dwTimeToStop = (DWORD) -1L; // very large
if (lpcs->sCapParms.fMCIControl) { // if MCI stop time not given, use lpcs->sCapParms.wTimeLimit
if (lpcs->sCapParms.dwMCIStopTime == lpcs->sCapParms.dwMCIStartTime) lpcs->sCapParms.dwMCIStopTime = lpcs->sCapParms.dwMCIStartTime + (DWORD) ((DWORD)1000 * lpcs->sCapParms.wTimeLimit);
dw = lpcs->sCapParms.dwMCIStopTime - lpcs->sCapParms.dwMCIStartTime;
if (lpcs->sCapParms.fLimitEnabled) dwTimeToStop = min (dw, dwTimeToStop); else dwTimeToStop = dw; }
//
// never ever try to capture more than the index size!
//
if (lpcs->fCapturingToDisk) { dw = muldiv32(lpcs->sCapParms.dwIndexSize, lpcs->sCapParms.dwRequestMicroSecPerFrame, 1000l);
dwTimeToStop = min (dw, dwTimeToStop); }
if (lpcs->sCapParms.fMCIControl) { fOK = FALSE; // Assume the worst
if (MCIDeviceOpen (lpcs)) { if (MCIDeviceSetPosition (lpcs, lpcs->sCapParms.dwMCIStartTime)) fOK = TRUE; } if (!fOK) { errorUpdateError (lpcs, IDS_CAP_MCI_CONTROL_ERROR); statusUpdateStatus(lpcs, NULL); // Clear status
lpcs-> dwReturn = IDS_CAP_MCI_CONTROL_ERROR; goto EarlyExit; } }
//
// If we're compressing while capturing, warm up the compressor
//
if (lpcs->CompVars.hic) { if (ICSeqCompressFrameStart(&lpcs->CompVars, lpcs->lpBitsInfo) == NULL) {
// !!! We're in trouble here!
dprintf("ICSeqCompressFrameStart failed !!!\n"); lpcs-> dwReturn = IDS_CAP_COMPRESSOR_ERROR; errorUpdateError (lpcs, IDS_CAP_COMPRESSOR_ERROR); goto EarlyExit; } // Kludge, offset the lpBitsOut ptr
// Compman allocates the compress buffer too large by
// 2048 + 16 so we will still have room
((LPBYTE) lpcs->CompVars.lpBitsOut) += 8; }
// No compression desired
if (!lpcs->CompVars.hic) WinAssert(lpcs->CompVars.lpbiOut == NULL);
// -------------------------------------------------------
// Open the output file
// -------------------------------------------------------
if (lpcs->fCapturingToDisk) { if (!AVIFileInit(lpcs)) { lpcs-> dwReturn = IDS_CAP_FILE_OPEN_ERROR; errorUpdateError (lpcs, IDS_CAP_FILE_OPEN_ERROR); goto EarlyExit; } }
/* Make sure the parent has been repainted */ UpdateWindow(lpcs->hwnd);
//
// call AVIInit() to get all the capture memory we will need
//
// Don't use DOS memory if capturing to Net
fVideoBuffersInDOSMem = lpcs->sCapParms.fUsingDOSMemory;
wError = AVIInit(lpcs);
if (wError && fVideoBuffersInDOSMem) { lpcs->sCapParms.fUsingDOSMemory = FALSE; wError = AVIInit(lpcs); }
if (wError) { /* Error in initalization - return */ errorUpdateError (lpcs, wError); AVIFini(lpcs); AVIFileFini(lpcs, TRUE /* fWroteJunkChunks */, TRUE /* fAbort */); statusUpdateStatus(lpcs, NULL); // Clear status
lpcs-> dwReturn = wError; goto EarlyExit; }
/* Click OK to capture string (must follow AVIInit) */ LoadString(lpcs->hInst, IDS_CAP_SEQ_MSGSTART, ach, sizeof(ach)); wsprintf(achMsg, ach, (LPSTR)lpcs->achFile);
statusUpdateStatus(lpcs, NULL);
// -------------------------------------------------------
// Ready to go, make the user click OK?
// -------------------------------------------------------
if (lpcs->sCapParms.fMakeUserHitOKToCapture && lpcs->fCapturingToDisk) { w = MessageBox(lpcs->hwnd, achMsg, "", MB_OKCANCEL | MB_ICONEXCLAMATION); if (w == IDCANCEL) { /* clean-up and get out */ AVIFini(lpcs); AVIFileFini(lpcs, TRUE /* fWroteJunkChunks */, TRUE /* fAbort */); statusUpdateStatus(lpcs, NULL); // Clear status
goto EarlyExit; } } // endif forcing user to hit OK
/* update the status, so the user knows how to stop */ statusUpdateStatus(lpcs, IDS_CAP_SEQ_MSGSTOP); UpdateWindow(lpcs->hwnd);
lpcs-> fCapturingNow = TRUE;
GetAsyncKeyState(lpcs->sCapParms.vKeyAbort); GetAsyncKeyState(VK_ESCAPE); GetAsyncKeyState(VK_LBUTTON); GetAsyncKeyState(VK_RBUTTON);
if (lpcs->sCapParms.fDisableWriteCache) wSmartDrv = SmartDrv(lpcs->achFile[0], (WORD)-1); // turn all off....
// Insert the digitization time
cic.fccInfoID = mmioFOURCC ('I','D','I','T'); time (<ime); cic.lpData = (LPSTR) ctime(<ime); cic.cbData = 26; SetInfoChunk (lpcs, &cic);
// -------------------------------------------------------
// Start MCI, Audio, and video streams
// -------------------------------------------------------
if (lpcs-> CallbackOnControl) { // Callback will preroll, then return on frame accurate postion
// The 1 indicates recording is about to start
// Callback can return FALSE to exit without capturing
if (!((*(lpcs->CallbackOnControl)) (lpcs->hwnd, CONTROLCALLBACK_PREROLL ))) { /* clean-up and get out */ AVIFini(lpcs); AVIFileFini(lpcs, TRUE /* fWroteJunkChunks */, TRUE /* fAbort */); statusUpdateStatus(lpcs, NULL); // Clear status
goto EarlyExit; } }
if (lpcs->sCapParms.fMCIControl) MCIDevicePlay (lpcs);
dwTimeStarted = timeGetTime();
if(lpcs->sCapParms.fCaptureAudio) waveInStart(lpcs->hWaveIn);
videoStreamStart(lpcs->hVideoIn);
// -------------------------------------------------------
// MAIN CAPTURE LOOP
// -------------------------------------------------------
fOK=TRUE; fStopping = FALSE; // TRUE when we need to stop
fStopped = FALSE; // TRUE if drivers notified we have stopped
lpcs->dwTimeElapsedMS = 0;
lpVidHdr = lpcs->alpVideoHdr[lpcs->iNextVideo]; lpWaveHdr = lpcs->alpWaveHdr[lpcs->iNextWave];
for (;;) {
// The INTEL driver uses the GetError message to
// process buffers, so call it often...
videoStreamGetError (lpcs->hVideoIn, &dwStreamError, &dwDriverDropCount);
// What time is it?
lpcs->dwTimeElapsedMS = timeGetTime() - dwTimeStarted;
// -------------------------------------------------------
// Is video buffer ready to be written?
// -------------------------------------------------------
if ((lpVidHdr->dwFlags & VHDR_DONE)) { if (lpVidHdr-> dwBytesUsed) { // Current time in milliseconds
dw = muldiv32 ((lpcs->dwVideoChunkCount + 1), lpcs->sCapParms.dwRequestMicroSecPerFrame, 1000); if (lpcs->CallbackOnVideoStream) (*(lpcs->CallbackOnVideoStream)) (lpcs->hwnd, lpVidHdr);
if (lpcs-> fCapturingToDisk) { if (lpcs->dwVideoChunkCount && (dw < lpVidHdr->dwTimeCaptured)) { // Has the capture device skipped frames?
// w = # of frames skipped
w = (WORD) muldiv32 ((lpVidHdr-> dwTimeCaptured - dw), 1000, lpcs->sCapParms.dwRequestMicroSecPerFrame); w = min (w, (sizeof (lpcs->DropFrame) / sizeof (RIFF) - sizeof (RIFF) ) ); lpcs->dwFramesDropped+= w; fOK = AVIWriteDummyFrames (lpcs, w);
if (!fOK) fStopping = TRUE; } // end if writing dummy frames
if (!AVIWriteVideoFrame (lpcs, lpVidHdr)) { fOK = FALSE; fStopping = TRUE; // "ERROR: Could not write to file."
errorUpdateError(lpcs, IDS_CAP_FILE_WRITE_ERROR); } else { if (!IndexVideo(lpcs, lpVidHdr-> dwBytesUsed, (BOOL) (lpVidHdr->dwFlags & VHDR_KEYFRAME))) fStopping = TRUE; } } // endif fCapturingToDisk
// Warning: Kludge to create frame chunk count when net capture
// follows.
else lpcs->dwVideoChunkCount++;
// -------------------------------------------------------
// if we have *nothing* to do paint or show status.
// -------------------------------------------------------
w = (lpcs->iNextVideo + 1) % lpcs->iNumVideo; if (!(lpcs->alpVideoHdr[w]-> dwFlags & VHDR_DONE)) { if (fTryToPaint && lpcs->dwVideoChunkCount && lpVidHdr-> dwFlags & VHDR_KEYFRAME) { fTryToPaint = DrawDibDraw(lpcs->hdd, hdc, 0, 0, rcDrawRect.right - rcDrawRect.left, rcDrawRect.bottom - rcDrawRect.top, /*lpcs->dxBits, lpcs->dyBits, */ (LPBITMAPINFOHEADER)lpcs->lpBitsInfo, lpVidHdr-> lpData, 0, 0, -1, -1, DDF_SAME_HDC | DDF_SAME_DIB | DDF_SAME_SIZE); } } // if there is still more time, (or at least every 100 frames)
// show status if we're not ending the capture
if ((!fStopping) && (lpcs-> fCapturingToDisk) && ((lpcs->dwVideoChunkCount && (lpcs->dwVideoChunkCount % 100 == 0)) || (!(lpcs->alpVideoHdr[w]-> dwFlags & VHDR_DONE)) ) ) {
// "Captured %ld frames (Dropped %ld) %d.%03d sec. Hit Escape to Stop"
statusUpdateStatus(lpcs, IDS_CAP_STAT_VIDEOCURRENT, lpcs->dwVideoChunkCount, lpcs->dwFramesDropped, (int)(lpcs-> dwTimeElapsedMS/1000), (int)(lpcs-> dwTimeElapsedMS%1000) ); } // endif next buffer not ready
} // endif any bytes used in the buffer
/* return the emptied buffer to the que */ lpVidHdr->dwFlags &= ~VHDR_DONE; if (videoStreamAddBuffer(lpcs->hVideoIn, lpVidHdr, sizeof (VIDEOHDR))) { fOK = FALSE; fStopping = TRUE; // "ERROR: Could not re-add buffer."
errorUpdateError (lpcs, IDS_CAP_VIDEO_ADD_ERROR); }
/* increment the next Video buffer pointer */ if (++lpcs->iNextVideo >= lpcs->iNumVideo) lpcs->iNextVideo = 0;
lpVidHdr = lpcs->alpVideoHdr[lpcs->iNextVideo]; }
if (lpcs-> CallbackOnYield) { // If the yield callback returns FALSE, abort
if (!((*(lpcs->CallbackOnYield)) (lpcs->hwnd))) fStopping = TRUE; }
// Don't do peekMessage yield for ACM
if (lpcs->sCapParms.fYield) { MSG msg;
if (PeekMessage(&msg,NULL,0,0,PM_REMOVE)) { // Kludge to get rid of timers from lpcs->hwnd
if (msg.message == WM_TIMER && msg.hwnd == lpcs->hwnd) ; else { TranslateMessage(&msg); DispatchMessage(&msg); } } }
if (lpcs-> CallbackOnControl) { // Outside routine is handling when to stop
// The CONTROLCALLBACK_CAPTURING indicates we're asking when to stop
if (!((*(lpcs->CallbackOnControl)) (lpcs->hwnd, CONTROLCALLBACK_CAPTURING ))) fStopping = TRUE; }
// -------------------------------------------------------
// Is audio buffer ready to be written?
// -------------------------------------------------------
if (lpcs->sCapParms.fCaptureAudio) { int iLastWave;
//
// we may need to yield for audio to get converted.
//
if (lpcs->fAudioYield) Yield();
//
// if all buffers are done, we have broke audio.
//
iLastWave = lpcs->iNextWave == 0 ? lpcs->iNumAudio -1 : lpcs->iNextWave-1;
if (!fStopping && lpcs->alpWaveHdr[iLastWave]->dwFlags & WHDR_DONE) lpcs->fAudioBreak = TRUE;
w = lpcs->iNumAudio; // don't get stuck here forever...
while (w && fOK && (lpWaveHdr-> dwFlags & WHDR_DONE)) { w--; if (lpWaveHdr-> dwBytesRecorded) { /* Chunk info is included in the wave data */ /* Reset Chunk Size in buffer */ ((LPRIFF)(lpWaveHdr->lpData))[-1].dwSize = lpWaveHdr-> dwBytesRecorded; if (lpcs-> CallbackOnWaveStream) { (*(lpcs->CallbackOnWaveStream)) (lpcs->hwnd, lpWaveHdr); } if (lpcs-> fCapturingToDisk) { if(!AVIWrite (lpcs, lpWaveHdr-> lpData - sizeof(RIFF), (lpWaveHdr-> dwBytesRecorded + sizeof (RIFF) + 1) & ~1L)) { fOK = FALSE; fStopping = TRUE; errorUpdateError (lpcs, IDS_CAP_FILE_WRITE_ERROR); } else { if (IndexAudio (lpcs, lpWaveHdr-> dwBytesRecorded)) lpcs->dwWaveBytes += lpWaveHdr-> dwBytesRecorded; else fStopping = TRUE; } } // endif capturing to disk
// Warning: Kludge to create wave chunk count when net capture
// follows.
else { lpcs->dwWaveChunkCount++; lpcs->dwWaveBytes += lpWaveHdr-> dwBytesRecorded; } } // endif dwBytesRecorded
lpWaveHdr-> dwBytesRecorded = 0; lpWaveHdr-> dwFlags &= ~WHDR_DONE;
/* return the emptied buffer to the que */ if(waveInAddBuffer(lpcs->hWaveIn, lpWaveHdr, sizeof(WAVEHDR))) { fOK = FALSE; fStopping = TRUE; errorUpdateError(lpcs, IDS_CAP_WAVE_ADD_ERROR); }
/* increment the next wave buffer pointer */ if(++lpcs->iNextWave >= lpcs->iNumAudio) lpcs->iNextWave = 0;
lpWaveHdr = lpcs->alpWaveHdr[lpcs->iNextWave];
} // endwhile buffer available
} // endif sound enabled
// -------------------------------------------------------
// is there any reason to stop?
// -------------------------------------------------------
if (lpcs->sCapParms.vKeyAbort) { if (GetAsyncKeyState(lpcs->sCapParms.vKeyAbort & 0x00ff) & 0x0001) { fT = TRUE; if (lpcs->sCapParms.vKeyAbort & 0x8000) // Ctrl?
fT = fT && (GetAsyncKeyState(VK_CONTROL) & 0x8000); if (lpcs->sCapParms.vKeyAbort & 0x4000) // Shift?
fT = fT && (GetAsyncKeyState(VK_SHIFT) & 0x8000); fStopping = fT; // User aborts
} } if (lpcs->sCapParms.fAbortLeftMouse) if (GetAsyncKeyState(VK_LBUTTON) & 0x0001) fStopping = TRUE; // User aborts
if (lpcs->sCapParms.fAbortRightMouse) if (GetAsyncKeyState(VK_RBUTTON) & 0x0001) fStopping = TRUE; // User aborts
if (lpcs-> fAbortCapture || lpcs-> fStopCapture) fStopping = TRUE; // Somebody above wants us to quit
if (lpcs-> dwTimeElapsedMS > dwTimeToStop) fStopping = TRUE; // all done
// -------------------------------------------------------
// Quit only when we have stopped, and
// no more buffers are pending from any device.
// -------------------------------------------------------
if (fStopped) { if (!(lpVidHdr-> dwFlags & VHDR_DONE)) { if (lpcs->sCapParms.fCaptureAudio) { if (!(lpWaveHdr-> dwFlags & WHDR_DONE)) break; } else break; } }
// -------------------------------------------------------
// Tell all the devices to stop
// -------------------------------------------------------
if (fStopping && !fStopped) { fStopped = TRUE;
DSTATUS(lpcs, "Stopping....");
if(lpcs->sCapParms.fCaptureAudio) { DSTATUS(lpcs, "Stopping Audio"); waveInStop(lpcs->hWaveIn); }
DSTATUS(lpcs, "Stopping Video"); videoStreamStop(lpcs->hVideoIn); // Stop everybody
dwTimeStopped = timeGetTime ();
if (lpcs->sCapParms.fMCIControl) { DSTATUS(lpcs, "Stopping MCI"); MCIDevicePause (lpcs); } DSTATUS(lpcs, "Stopped");
SetCursor(lpcs->hWaitCursor); // Force cursor back to hourglass
}
if (fStopping) { // "Finished capture, now writing frame %ld"
if (fOK) { statusUpdateStatus(lpcs, IDS_CAP_STAT_CAP_FINI, lpcs->dwVideoChunkCount); } else { // Exit if problems
statusUpdateStatus(lpcs, IDS_CAP_RECORDING_ERROR2); break; } }
} // end of forever
// -------------------------------------------------------
// END OF MAIN CAPTURE LOOP
// -------------------------------------------------------
if (lpcs->sCapParms.fDisableWriteCache) SmartDrv(lpcs->achFile[0], wSmartDrv); // turn Smartdrive back on
/* eat any keys that have been pressed */ while(GetKey(FALSE)) ;
AVIFini(lpcs); // does the Reset, and frees all buffers
AVIFileFini(lpcs, TRUE /* fWroteJunkChunks */, FALSE /* fAbort */);
// This is the corrected capture duration, based on audio samples
lpcs->dwTimeElapsedMS = lpcs->dwActualMicroSecPerFrame * lpcs->dwVideoChunkCount / 1000;
/* Notify if there was an error while recording */
if(!fOK) { errorUpdateError (lpcs, IDS_CAP_RECORDING_ERROR); }
if (lpcs-> fCapturingToDisk) { if (lpcs->dwVideoChunkCount) dw = muldiv32(lpcs->dwVideoChunkCount,1000000,lpcs-> dwTimeElapsedMS); else dw = 0; // The muldiv32 doesn't give 0 if numerator is zero
if(lpcs->sCapParms.fCaptureAudio) { // "Captured %d.%03d sec. %ld frames (%ld dropped) (%d.%03d fps). %ld audio bytes (%d.%03d sps)"
statusUpdateStatus(lpcs, IDS_CAP_STAT_VIDEOAUDIO, (WORD)(lpcs-> dwTimeElapsedMS/1000), (WORD)(lpcs-> dwTimeElapsedMS%1000), lpcs->dwVideoChunkCount, lpcs->dwFramesDropped, (WORD)(dw / 1000), (WORD)(dw % 1000), lpcs->dwWaveBytes, (WORD) lpcs->lpWaveFormat->nSamplesPerSec / 1000, (WORD) lpcs->lpWaveFormat->nSamplesPerSec % 1000); } else { // "Captured %d.%03d sec. %ld frames (%ld dropped) (%d.%03d fps)."
statusUpdateStatus(lpcs, IDS_CAP_STAT_VIDEOONLY, (WORD)(lpcs-> dwTimeElapsedMS/1000), (WORD)(lpcs-> dwTimeElapsedMS%1000), lpcs->dwVideoChunkCount, lpcs->dwFramesDropped, (WORD)(dw / 1000), (WORD)(dw % 1000)); } } // endif capturing to disk (no warnings or errors if to net)
// No frames captured, warn user that interrupts are probably not enabled.
if (fOK && (lpcs->dwVideoChunkCount == 0)) { errorUpdateError (lpcs, IDS_CAP_NO_FRAME_CAP_ERROR); } // No audio captured, (but enabled), warn user audio card is hosed
else if (fOK && lpcs->sCapParms.fCaptureAudio && (lpcs->dwWaveBytes == 0)) { errorUpdateError (lpcs, IDS_CAP_NO_AUDIO_CAP_ERROR); } // Audio underrun, inform user
else if (fOK && lpcs->sCapParms.fCaptureAudio && lpcs->fAudioBreak) { errorUpdateError (lpcs, IDS_CAP_AUDIO_DROP_ERROR); }
// If frames dropped, or changed capture rate, warn the user
else if (fOK && lpcs->dwVideoChunkCount && lpcs->fCapturingToDisk) {
// Warn user if dropped > 10% (default) of the frames
if ((DWORD)100 * lpcs->dwFramesDropped / lpcs->dwVideoChunkCount > lpcs-> sCapParms.wPercentDropForError) {
// "%ld of %ld frames (%d.%03d\%) dropped during capture."
errorUpdateError (lpcs, IDS_CAP_STAT_FRAMESDROPPED, lpcs->dwFramesDropped, lpcs->dwVideoChunkCount, (WORD)(muldiv32(lpcs->dwFramesDropped,10000,lpcs->dwVideoChunkCount)/100), (WORD)(muldiv32(lpcs->dwFramesDropped,10000,lpcs->dwVideoChunkCount)%100) ); } }
EarlyExit:
//
// If we were compressing while capturing, close it down
//
if (lpcs->CompVars.hic) { // Kludge, reset the lpBitsOut pointer
if (lpcs->CompVars.lpBitsOut) ((LPBYTE) lpcs->CompVars.lpBitsOut) -= 8; ICSeqCompressFrameEnd(&lpcs->CompVars); }
if (fTryToPaint) { if (hpalT) SelectPalette(hdc, hpalT, FALSE); ReleaseDC (lpcs->hwnd, hdc); }
if (lpcs->sCapParms.fMCIControl) MCIDeviceClose (lpcs);
// Let the user see where capture stopped
if ((!lpcs->fLiveWindow) && (!lpcs->fOverlayWindow)) videoFrame( lpcs->hVideoIn, &lpcs->VidHdr ); InvalidateRect( lpcs->hwnd, NULL, TRUE);
SetCursor(hOldCursor);
lpcs->fCapFileExists = (lpcs-> dwReturn == DV_ERR_OK); lpcs->fCapturingNow = FALSE;
statusUpdateStatus(lpcs, IDS_CAP_END); // Always the last message
return; }
// Returns TRUE if the capture task was created, or
// capture completed OK.
BOOL AVICapture (LPCAPSTREAM lpcs) { WORD w; CAPINFOCHUNK cic; char szSMPTE[40];
if (lpcs-> fCapturingNow) return IDS_CAP_VIDEO_OPEN_ERROR;
lpcs-> fStopCapture = FALSE; lpcs-> fAbortCapture = FALSE; lpcs-> hTaskCapture = NULL; lpcs-> dwReturn = 0;
// Clear any SMPTE info chunk
cic.fccInfoID = mmioFOURCC ('I','S','M','T'); cic.lpData = NULL; cic.cbData = 0; SetInfoChunk (lpcs, &cic);
#if 1
// And get ready to write a SMPTE info chunk
if (lpcs->sCapParms.fMCIControl) { // create SMPTE string
TimeMSToSMPTE (lpcs->sCapParms.dwMCIStartTime, (LPSTR) szSMPTE); cic.lpData = szSMPTE; cic.cbData = lstrlen (szSMPTE) + 1; SetInfoChunk (lpcs, &cic); } #endif
// Use an MCI device to do step capture capture???
if (lpcs->sCapParms.fStepMCIDevice && lpcs->sCapParms.fMCIControl) { if (lpcs->sCapParms.fYield) { w = (WORD) mmTaskCreate((LPTASKCALLBACK) MCIStepCapture, &lpcs->hTaskCapture, (DWORD) lpcs); // if task creation failed, turn off the capturing flag
if (w != 0) lpcs->fCapturingNow = FALSE; return ((BOOL) !w); } else { MCIStepCapture (lpcs); return ((BOOL) !lpcs->dwReturn); } }
// No MCI device, just a normal streaming capture
else if (lpcs->sCapParms.fYield) { w = (WORD) mmTaskCreate((LPTASKCALLBACK) AVICapture1, &lpcs->hTaskCapture, (DWORD) lpcs); // if task creation failed, turn off the capturing flag
if (w != 0) lpcs->fCapturingNow = FALSE; return ((BOOL) !w); } else { AVICapture1 (lpcs); return ((BOOL) !lpcs->dwReturn); } }
|