2217 lines
72 KiB
C
2217 lines
72 KiB
C
|
/****************************************************************************
|
|||
|
*
|
|||
|
* 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);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|