windows-nt/Source/XPSP1/NT/multimedia/media/avi/avicap.io/capavi.c

2665 lines
86 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/****************************************************************************
*
* capavi.c
*
* Main video capture module.
*
* Microsoft Video for Windows Sample Capture Class
*
* Copyright (c) 1992 - 1995 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.
*
***************************************************************************/
#define INC_OLE2
#pragma warning(disable:4103)
#include <windows.h>
#include <windowsx.h>
#include <win32.h>
#include <mmsystem.h>
#include <vfw.h>
#include <mmreg.h>
#include <mmddk.h>
#include "ivideo32.h"
#include "mmdebug.h"
#ifdef USE_ACM
#include <msacm.h>
#endif
#include "avicapi.h"
#include "time.h"
#define JMK_HACK_TIMERS TRUE
#ifdef JMK_HACK_TIMERS
#define _INC_MMTIMERS_CODE_ TRUE
#define CLIPBOARDLOGSIZE 1000
#ifndef MAKEFOURCC
#define MAKEFOURCC(a,b,c,d) ((DWORD)(a) | ((DWORD)(b) << 8) | ((DWORD)(c) << 16) | ((DWORD)(d) << 24))
#endif
#define RIFFTYPE(dw) (((dw & 0xFF) << 24) | ((dw & 0xFF00) << 8) | ((dw & 0xFF0000) >> 8) | ((dw & 0xFF000000) >> 24))
#include "mmtimers.h"
typedef struct _timerstuff {
DWORD dwFrameTickTime; // What we think the current frame time should be
DWORD dwFrameStampTime; // Stamped in the VIDEOHDR
DWORD dwTimeWritten; // Time WriteFile called
DWORD dwTimeToWrite; // Time WriteFile returned
WORD nFramesAppended; // Accumulated appended dropped frames
WORD nDummyFrames; // frames calc'ed as dropped
DWORD dwVideoChunkCount; // current 'frame'
WORD nAudioIndex; // next audio buffer
WORD nVideoIndex; // next video buffer
BOOL bPending;
WORD nSleepCount;
DWORD dwSleepBegin;
DWORD dwSleepEnd;
};
STATICDT PCTIMER pctWriteBase;
STATICDT struct _timerstuff * pCurTimerStuff;
STATICDT struct _timerstuff * pTimerStuff;
STATICDT HGLOBAL hMemTimers;
STATICDT struct _timerriff {
FOURCC fccRIFF; // 'RIFF'
DWORD cbTotal; // total (inclusive) size of riff data
FOURCC fccJMKD; // 'JMKD' data type identifier
DWORD fccVCHD; // 'VCHD' capture data header
DWORD cbVCHD; // sizeof vchd data
struct _vchd {
DWORD nPrio;
DWORD dwFramesCaptured;
DWORD dwFramesDropped;
DWORD dwDropFramesAppended;
DWORD dwDropFramesNotAppended;
DWORD dwTimerFrequency;
DWORD dwSpare[2];
CAPTUREPARMS cap;
BITMAPINFOHEADER bmih;
DWORD nMaxVideoBuffers;
struct _thkvideohdr {
VIDEOHDR vh;
LPBYTE p32Buff;
DWORD p16Alloc;
DWORD dwMemHandle;
DWORD dwReserved;
} atvh[64];
} vchd;
DWORD fccChunk; // chunk data type tag
DWORD cbChunk; // non-inclusive size of chunk data
} * pTimerRiff;
STATICDT UINT nTimerIndex;
STATICDT UINT nSleepCount;
#endif
#ifdef _DEBUG
#define DSTATUS(lpcs, sz) statusUpdateStatus(lpcs, IDS_CAP_INFO, (LPTSTR) TEXT(sz))
#else
#define DSTATUS(lpcs, sz)
#endif
// Allocate memory on a sector boundary
LPVOID FAR PASCAL AllocSectorAlignedMem (DWORD dwRequest, DWORD dwAlign)
{
LPVOID pbuf;
dwRequest = (DWORD) ROUNDUPTOSECTORSIZE (dwRequest, dwAlign) + dwAlign; // round up to next page boundary
pbuf = VirtualAlloc (NULL, dwRequest,
MEM_COMMIT | MEM_RESERVE,
PAGE_READWRITE);
AuxDebugEx(4, DEBUGLINE "Allocated %d bytes of sector aligned memory at %8x\r\n", dwRequest, pbuf);
return pbuf;
}
void FAR PASCAL FreeSectorAlignedMem (LPVOID pbuf)
{
// the pointer we free had better be aligned on at least a 256 byte
// boundary
//
assert (!((DWORD_PTR)pbuf & 255));
VirtualFree ((LPVOID)((DWORD_PTR)pbuf & ~255), 0, MEM_RELEASE);
}
#define ONEMEG (1024L * 1024L)
DWORDLONG GetFreePhysicalMemory(void)
{
MEMORYSTATUSEX ms;
ms.dwLength = sizeof(ms);
GlobalMemoryStatusEx(&ms);
if (ms.ullTotalPhys > 8L * ONEMEG)
return ms.ullTotalPhys - ONEMEG * 4;
return(ms.ullTotalPhys /2);
}
// ****************************************************************
// ******************** 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.
UINT AVIAudioInit (LPCAPSTREAM lpcs)
{
int i;
LPVOID pHdr;
LPVOID p;
if (lpcs->sCapParms.wNumAudioRequested == 0)
lpcs->sCapParms.wNumAudioRequested = DEF_WAVE_BUFFERS;
// .5 second of audio per buffer (or 10K, whichever is larger)
if (lpcs->sCapParms.dwAudioBufferSize == 0)
lpcs->dwWaveSize = CalcWaveBufferSize (lpcs);
else {
lpcs->dwWaveSize = 0;
if (lpcs->lpWaveFormat)
lpcs->dwWaveSize = lpcs->sCapParms.dwAudioBufferSize;
}
// Alloc the wave memory
for(i = 0; i < (int)lpcs->sCapParms.wNumAudioRequested; i++) {
pHdr = GlobalAllocPtr(GPTR, sizeof(WAVEHDR));
if (pHdr == NULL)
break;
lpcs->alpWaveHdr[i] = pHdr;
p = AllocSectorAlignedMem( sizeof(RIFF) + lpcs->dwWaveSize, lpcs->dwBytesPerSector);
if (p == NULL) {
GlobalFreePtr (pHdr);
lpcs->alpWaveHdr[i] = NULL;
break;
}
lpcs->alpWaveHdr[i]->lpData = (LPBYTE)p + sizeof(RIFF);
lpcs->alpWaveHdr[i]->dwBufferLength = lpcs->dwWaveSize;
lpcs->alpWaveHdr[i]->dwBytesRecorded = 0;
lpcs->alpWaveHdr[i]->dwUser = 0;
lpcs->alpWaveHdr[i]->dwFlags = 0;
lpcs->alpWaveHdr[i]->dwLoops = 0;
((LPRIFF)p)->dwType = MAKEAVICKID(cktypeWAVEbytes, 1);
((LPRIFF)p)->dwSize = lpcs->dwWaveSize;
}
lpcs->iNumAudio = i;
return ((lpcs->iNumAudio == 0) ? IDS_CAP_WAVE_ALLOC_ERROR : 0);
}
//
// AVI AudioFini - UnPrepares headers
// This routine is also used by MCI capture.
// Returns: 0 on success, otherwise an error code.
UINT AVIAudioFini (LPCAPSTREAM lpcs)
{
int ii;
/* free headers and data */
for (ii=0; ii < MAX_WAVE_BUFFERS; ++ii) {
if (lpcs->alpWaveHdr[ii]) {
if (lpcs->alpWaveHdr[ii]->lpData)
FreeSectorAlignedMem((LPBYTE)lpcs->alpWaveHdr[ii]->lpData - sizeof (RIFF));
GlobalFreePtr(lpcs->alpWaveHdr[ii]);
lpcs->alpWaveHdr[ii] = 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.
UINT AVIAudioPrepare (LPCAPSTREAM lpcs)
{
UINT uiError;
int ii;
/* See if we can open that format for input */
// register event callback to avoid polling
uiError = waveInOpen(&lpcs->hWaveIn,
WAVE_MAPPER, lpcs->lpWaveFormat,
(DWORD_PTR) lpcs->hCaptureEvent, 0, CALLBACK_EVENT );
if (uiError != MMSYSERR_NOERROR)
return IDS_CAP_WAVE_OPEN_ERROR;
lpcs->fAudioYield = FALSE; // ACM is separate thread, don't yield
lpcs->fAudioBreak = FALSE;
DPF("AudioYield = %d", lpcs->fAudioYield);
for (ii = 0; ii < (int)lpcs->sCapParms.wNumAudioRequested; ++ii) {
if (waveInPrepareHeader (lpcs->hWaveIn, lpcs->alpWaveHdr[ii],
sizeof(WAVEHDR)))
return IDS_CAP_WAVE_ALLOC_ERROR;
if (waveInAddBuffer (lpcs->hWaveIn, lpcs->alpWaveHdr[ii],
sizeof(WAVEHDR)))
return IDS_CAP_WAVE_ALLOC_ERROR;
AuxDebugEx(3, DEBUGLINE "Added wave buffer %d (%8x)\r\n", ii, lpcs->alpWaveHdr[ii]);
}
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.
UINT AVIAudioUnPrepare (LPCAPSTREAM lpcs)
{
int ii;
if (lpcs->hWaveIn)
{
waveInReset(lpcs->hWaveIn);
// unprepare any headers that have been prepared
//
for (ii=0; ii < lpcs->iNumAudio; ++ii)
if (lpcs->alpWaveHdr[ii] &&
(lpcs->alpWaveHdr[ii]->dwFlags & WHDR_PREPARED))
waveInUnprepareHeader (lpcs->hWaveIn,
lpcs->alpWaveHdr[ii],
sizeof(WAVEHDR));
waveInClose(lpcs->hWaveIn);
lpcs->hWaveIn = NULL;
}
return 0;
}
// ****************************************************************
// ******************** Video Buffer Control **********************
// ****************************************************************
#if defined CHICAGO
// Win95 capavi code
// AVIVideoInit - Allocates, and initialize buffers for video capture.
// This routine is also used by MCI capture.
// Returns: 0 on success, otherwise an error code.
UINT AVIVideoInit (LPCAPSTREAM lpcs)
{
UINT iMaxVideo;
DWORD mmr;
LPTHKVIDEOHDR ptvh;
UINT ii;
DWORD cbVideo;
lpcs->iNumVideo = 0;
lpcs->iNextVideo = 0;
lpcs->dwVideoChunkCount = 0;
lpcs->dwFramesDropped = 0;
lpcs->fBuffersOnHardware = FALSE;
// When performing MCI step capture, buffer array is not used
if (lpcs->sCapParms.fStepMCIDevice)
return 0;
cbVideo = ROUNDUPTOSECTORSIZE (lpcs->lpBitsInfo->bmiHeader.biSizeImage
+ sizeof(RIFF),
lpcs->dwBytesPerSector)
+ lpcs->dwBytesPerSector;
// If the user hasn't specified the number of video buffers to use,
// assume the minimum
if (lpcs->sCapParms.wNumVideoRequested == 0) {
iMaxVideo = lpcs->sCapParms.wNumVideoRequested = MIN_VIDEO_BUFFERS;
lpcs->fCaptureFlags |= CAP_fDefaultVideoBuffers;
} else {
// use the number of video buffers that the user requested
// or the maximum that will fit in memory.
//
iMaxVideo = min (MAX_VIDEO_BUFFERS, lpcs->sCapParms.wNumVideoRequested);
}
if (iMaxVideo > 1)
{
DWORDLONG dwFreeMem;
DWORDLONG dwUserRequests;
DWORDLONG dwAudioMem;
// 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
+ cbVideo * 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) / cbVideo);
iMaxVideo = min (MAX_VIDEO_BUFFERS, iMaxVideo);
dprintf("iMaxVideo = %d\n", iMaxVideo);
}
}
mmr = vidxAllocHeaders(lpcs->hVideoIn, iMaxVideo, &ptvh);
if (mmr != MMSYSERR_NOERROR)
return IDS_CAP_VIDEO_ALLOC_ERROR;
AuxDebugEx (3, DEBUGLINE "vidxAllocHdrs returned ptvh=%X\r\n", ptvh);
AuxDebugDump (8, ptvh, sizeof(*ptvh) * iMaxVideo);
for (ii = 0; ii < iMaxVideo; ++ii)
{
LPVIDEOHDR pvh = NULL;
LPRIFF priff;
// in chicago we let the thunk layer allocate memory
// so that we can be assured that the memory can be easily
// thunked.
//
// the pointer will be rounded up to a sector size boundary
//
mmr = vidxAllocBuffer (lpcs->hVideoIn, ii, &ptvh, cbVideo);
if ((mmr != MMSYSERR_NOERROR) || (ptvh == NULL))
break;
lpcs->alpVideoHdr[ii] = pvh = &ptvh->vh;
// vidxAllocBuffer actually returns a couple of extra fields
// after the video header. the first of these holds the
// linear address of the buffer.
//
priff = (LPVOID) ROUNDUPTOSECTORSIZE (ptvh->p32Buff, lpcs->dwBytesPerSector);
#ifdef DEBUG
{
LPBYTE pb = (LPVOID)ptvh->p32Buff;
AuxDebugEx (4, DEBUGLINE "buffer[%d] at %x linear. Doing touch test\r\n",
ii, ptvh->p32Buff);
pb[0] = 0;
pb[cbVideo-1] = 0;
}
#endif
// write the riff header for this chunk.
//
priff->dwType = MAKEAVICKID(cktypeDIBbits, 0);
if (lpcs->lpBitsInfo->bmiHeader.biCompression == BI_RLE8)
priff->dwType = MAKEAVICKID(cktypeDIBcompressed, 0);
priff->dwSize = lpcs->lpBitsInfo->bmiHeader.biSizeImage;
// init the video header
//
pvh->lpData = (LPVOID)(priff + 1);
pvh->dwBufferLength = priff->dwSize;
pvh->dwBytesUsed = 0;
pvh->dwTimeCaptured = 0;
pvh->dwUser = 0;
pvh->dwFlags = 0;
AuxDebugEx (4, DEBUGLINE "lpVideoHdr[%d]==%X\r\n", ii, lpcs->alpVideoHdr[ii]);
AuxDebugDump (8, lpcs->alpVideoHdr[ii], sizeof(*ptvh));
}
lpcs->iNumVideo = ii;
lpcs->cbVideoAllocation = cbVideo;
dprintf("cbVideo = %ld \n", cbVideo);
dprintf("iNumVideo Allocated = %d \n", lpcs->iNumVideo);
return lpcs->iNumVideo ? 0 : IDS_CAP_VIDEO_ALLOC_ERROR;
}
//
// 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.
UINT AVIVideoPrepare (LPCAPSTREAM lpcs)
{
int ii;
// When performing MCI step capture, buffer array is not used
if (lpcs->sCapParms.fStepMCIDevice)
return 0;
#ifdef JMK_HACK_CHECKHDR
{
LPTHKVIDEOHDR lptvh = (LPVOID)lpcs->alpVideoHdr[0];
if (HIWORD(lptvh->vh.lpData) != HIWORD(lptvh->p32Buff))
{
AuxDebugEx (0, DEBUGLINE "before stream init: hdr trouble\r\n");
AuxDebugEx (0, DEBUGLINE "iNext=%d, ptvh=%X\r\n", lpcs->iNextVideo, lptvh);
AuxDebugDump (0, lptvh, sizeof(*lptvh));
AuxDebugEx (0, DEBUGLINE "alpVideoHdrs=%X\r\n", lpcs->alpVideoHdr);
AuxDebugDump (0, lpcs->alpVideoHdr, sizeof(lpcs->alpVideoHdr[0]) * 8);
INLINE_BREAK;
return IDS_CAP_VIDEO_OPEN_ERROR;
}
}
#endif
// Open the video stream, setting the capture rate
//
if (videoStreamInit(lpcs->hVideoIn,
lpcs->sCapParms.dwRequestMicroSecPerFrame,
lpcs->hRing0CapEvt,
0,
CALLBACK_EVENT))
{
dprintf("cant open video device!\n");
return IDS_CAP_VIDEO_OPEN_ERROR;
}
#ifdef JMK_HACK_CHECKHDR
{
LPTHKVIDEOHDR lptvh = (LPVOID)lpcs->alpVideoHdr[0];
if (HIWORD(lptvh->vh.lpData) != HIWORD(lptvh->p32Buff))
{
AuxDebugEx (0, DEBUGLINE "after stream init: hdr trouble\r\n");
AuxDebugEx (0, DEBUGLINE "iNext=%d, ptvh=%X\r\n", lpcs->iNextVideo, lptvh);
AuxDebugDump (0, lptvh, sizeof(*lptvh));
AuxDebugEx (0, DEBUGLINE "alpVideoHdrs=%X\r\n", lpcs->alpVideoHdr);
AuxDebugDump (0, lpcs->alpVideoHdr, sizeof(lpcs->alpVideoHdr[0]) * 8);
INLINE_BREAK;
return IDS_CAP_VIDEO_OPEN_ERROR;
}
}
#endif
// Prepare (lock) the buffers, and give them to the device
//
for (ii = 0; ii < lpcs->iNumVideo; ++ii)
{
if (vidxAddBuffer (lpcs->hVideoIn,
lpcs->alpVideoHdr[ii],
sizeof(VIDEOHDR)))
{
lpcs->iNumVideo = ii;
dprintf("**** could only prepare %d Video buffers!\n", lpcs->iNumVideo);
break;
}
}
#ifdef JMK_HACK_CHECKHDR
{
LPTHKVIDEOHDR lptvh = (LPVOID)lpcs->alpVideoHdr[0];
if (IsBadWritePtr (lptvh, sizeof(*lptvh)) ||
HIWORD(lptvh->vh.lpData) != HIWORD(lptvh->p16Alloc))
{
AuxDebugEx (0, DEBUGLINE "after add buffers: hdr trouble\r\n");
AuxDebugEx (0, DEBUGLINE "iNext=%d, ptvh=%X\r\n", lpcs->iNextVideo, lptvh);
AuxDebugDump (0, lptvh, sizeof(*lptvh));
AuxDebugEx (0, DEBUGLINE "alpVideoHdrs=%X\r\n", lpcs->alpVideoHdr);
AuxDebugDump (0, lpcs->alpVideoHdr, sizeof(lpcs->alpVideoHdr[0]) * 8);
INLINE_BREAK;
return IDS_CAP_VIDEO_OPEN_ERROR;
}
}
#endif
return 0;
}
#else // code below is !CHICAGO
// this structure is used to keep track of memory allocation
// for video buffers used in capture. it is allocated when
// allocating a videohdr would be called for
//
typedef struct _cap_videohdr {
VIDEOHDR vh;
LPBYTE pAlloc; // address of allocated buffer
DWORD dwMemIdent; // identity of allocation (used in Chicago)
DWORD dwReserved; // used in chicago
BOOL bHwBuffer; // TRUE if buffer is allocated using videoStreamAllocBuffer
} CAPVIDEOHDR, FAR *LPCAPVIDEOHDR;
// AVIVideoInit - Allocates, and initialize buffers for video capture.
// This routine is also used by MCI capture.
// Returns: 0 on success, otherwise an error code.
UINT AVIVideoInit (LPCAPSTREAM lpcs)
{
int iMaxVideo;
int ii;
LPCAPVIDEOHDR pcvh;
LPVOID pbuf;
DWORD cbVideo;
BOOL fAllowHardwareBuffers;
//#define SINGLEHEADERBLOCK
lpcs->iNumVideo = 0;
lpcs->iNextVideo = 0;
lpcs->dwVideoChunkCount = 0;
lpcs->dwFramesDropped = 0;
lpcs->fBuffersOnHardware = FALSE;
fAllowHardwareBuffers = GetProfileIntA ("Avicap32", "AllowHardwareBuffers", TRUE);
// When performing MCI step capture, buffer array is not used
if (lpcs->sCapParms.fStepMCIDevice)
return 0;
cbVideo = (DWORD) ROUNDUPTOSECTORSIZE (lpcs->lpBitsInfo->bmiHeader.biSizeImage
+ sizeof(RIFF),
lpcs->dwBytesPerSector)
+ lpcs->dwBytesPerSector;
// If the user hasn't specified the number of video buffers to use,
// assume the minimum
if (lpcs->sCapParms.wNumVideoRequested == 0) {
UINT cDefaultVideoBuffers = GetProfileIntA ("Avicap32", "nVideoBuffers", MIN_VIDEO_BUFFERS);
cDefaultVideoBuffers = min(MAX_VIDEO_BUFFERS, max(MIN_VIDEO_BUFFERS, cDefaultVideoBuffers));
iMaxVideo = lpcs->sCapParms.wNumVideoRequested = cDefaultVideoBuffers;
lpcs->fCaptureFlags |= CAP_fDefaultVideoBuffers;
} else {
// use the number of video buffers that the user requested
// or the maximum that will fit in memory.
//
iMaxVideo = min (MAX_VIDEO_BUFFERS, lpcs->sCapParms.wNumVideoRequested);
}
// Post VFW 1.1a, see if the driver can allocate memory
//
#ifdef ALLOW_HW_BUFFERS
if (fAllowHardwareBuffers && (videoStreamAllocBuffer (lpcs->hVideoIn, (LPVOID *) &pbuf, cbVideo)
== DV_ERR_OK))
{
DWORD dwRet;
dprintf("Allocated test h/w buffer at address %8x, size %d bytes", pbuf, cbVideo);
lpcs->fBuffersOnHardware = TRUE;
dwRet = videoStreamFreeBuffer (lpcs->hVideoIn, pbuf);
dprintf("Freed test h/w buffer at address %8x, retcode 0x%x", pbuf, dwRet);
}
else
#endif
{
DWORDLONG dwFreeMem;
DWORDLONG dwUserRequests;
DWORDLONG dwAudioMem;
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
+ cbVideo * 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) / cbVideo);
iMaxVideo = min (MAX_VIDEO_BUFFERS, iMaxVideo);
dprintf("iMaxVideo = %d\n", iMaxVideo);
}
}
#ifdef SINGLEHEADERBLOCK
pcvh = GlobalAllocPtr (GMEM_MOVEABLE, iMaxVideo * sizeof(CAPVIDEOHDR));
// note: pcvh is freed by referencing through alpVideoHdr[0]
if ( ! pcvh)
return IDS_CAP_VIDEO_ALLOC_ERROR;
AuxDebugEx (3, DEBUGLINE "allocated video headers pcvh=%X\r\n", pcvh);
#endif
// Set up the buffers presuming fixed size DIBs and Junk chunks
// These will be modified later if the device provides compressed data
for (ii = 0; ii < iMaxVideo; ++ii)
{
LPVIDEOHDR pvh = NULL;
LPRIFF priff;
#ifndef SINGLEHEADERBLOCK
pcvh = (LPCAPVIDEOHDR)GlobalAllocPtr(GMEM_MOVEABLE, sizeof(CAPVIDEOHDR));
if (pcvh== NULL)
break;
lpcs->alpVideoHdr[ii] = (LPVIDEOHDR)pcvh;
ZeroMemory(pcvh, sizeof (CAPVIDEOHDR));
#endif
#ifdef ALLOW_HW_BUFFERS
//
// for the first buffer, always try to allocate on hardware,
// NO. If we are not to use hardware buffers, then do not use them.
// if that fails, grab virtual memory for the buffer.
// for all but the first buffer, we use whatever worked for
// the first buffer, and if that fails. we stop allocating buffers
//
if (lpcs->fBuffersOnHardware)
{
MMRESULT mmr;
pbuf = NULL;
mmr = videoStreamAllocBuffer (lpcs->hVideoIn, (LPVOID) &pbuf, cbVideo);
if ((mmr != MMSYSERR_NOERROR) || (pbuf == NULL))
{
if (0 == ii)
break; // nothing allocated
dprintf("Failed to allocate hardware buffer %d, rc=0x%x", ii, mmr);
// if the user did not ask for a specific number of buffers,
// or the hardware is set up to work with ONLY hardware
// allocated buffers, take what we've got and work with that.
if ((lpcs->fCaptureFlags & CAP_fDefaultVideoBuffers)
|| (GetProfileIntA ("Avicap32", "HardwareBuffersOnly", FALSE)))
{
break;
}
lpcs->fBuffersOnHardware = FALSE;
// use normal memory for the remaining video buffers.
pbuf = AllocSectorAlignedMem (cbVideo, lpcs->dwBytesPerSector);
}
else {
lpcs->fBuffersOnHardware = TRUE;
dprintf("Allocated hardware buffer %d at address %8x", ii, pbuf);
}
}
else
pbuf = AllocSectorAlignedMem (cbVideo, lpcs->dwBytesPerSector);
#else ! dont allow hw buffers
pbuf = AllocSectorAlignedMem (cbVideo, lpcs->dwBytesPerSector);
#endif // ALLOW_HW_BUFFERS
if (pbuf == NULL) {
#ifndef SINGLEHEADERBLOCK
GlobalFreePtr(pcvh);
lpcs->alpVideoHdr[ii] = NULL;
#endif
break;
}
// save the original allocation pointer to the buffer
// in the extra fields of the capture header. also remember
// whether we got the buffer from the driver or not
//
#ifndef SINGLEHEADERBLOCK
pcvh->pAlloc = pbuf;
pcvh->bHwBuffer = lpcs->fBuffersOnHardware;
lpcs->alpVideoHdr[ii] = pvh = &pcvh->vh;
#else
pcvh[ii].pAlloc = pbuf;
pcvh[ii].bHwBuffer = lpcs->fBuffersOnHardware;
lpcs->alpVideoHdr[ii] = pvh = &pcvh[ii].vh;
#endif
priff = (LPVOID) ROUNDUPTOSECTORSIZE (pbuf, lpcs->dwBytesPerSector);
// write the riff header for this frame
//
priff->dwType = MAKEAVICKID(cktypeDIBbits, 0);
if (lpcs->lpBitsInfo->bmiHeader.biCompression == BI_RLE8)
priff->dwType = MAKEAVICKID(cktypeDIBcompressed, 0);
priff->dwSize = lpcs->lpBitsInfo->bmiHeader.biSizeImage;
// fill in the video hdr for this frame
//
pvh->lpData = (LPVOID)(priff + 1);
pvh->dwBufferLength = priff->dwSize;
pvh->dwBytesUsed = 0;
pvh->dwTimeCaptured = 0;
pvh->dwUser = 0;
pvh->dwFlags = 0;
AuxDebugEx (4, DEBUGLINE "lpVideoHdr[%d]==%X\r\n", ii, lpcs->alpVideoHdr[ii]);
AuxDebugDump (8, lpcs->alpVideoHdr[ii], sizeof(*pcvh));
}
lpcs->iNumVideo = ii;
lpcs->cbVideoAllocation = cbVideo;
// if we did not create even a single buffer, free the headers
//
#ifdef SINGLEHEADERBLOCK
if ( ! lpcs->iNumVideo)
GlobalFreePtr (pcvh);
#else
// we allocate video headers as we proceed. There is nothing to free
#endif
#ifdef ALLOW_HW_BUFFERS
if (lpcs->fBuffersOnHardware)
dprintf("HARDWARE iNumVideo Allocated = %d \n", lpcs->iNumVideo);
else
#endif
dprintf("HIGH iNumVideo Allocated = %d \n", lpcs->iNumVideo);
return lpcs->iNumVideo ? 0 : IDS_CAP_VIDEO_ALLOC_ERROR;
}
void CALLBACK
VideoCallback(
HVIDEO hvideo,
UINT msg,
DWORD_PTR dwInstance,
DWORD_PTR lParam1,
DWORD_PTR lParam2
)
{
LPCAPSTREAM lpcs = (LPCAPSTREAM) dwInstance;
if (lpcs && lpcs->hCaptureEvent) {
SetEvent(lpcs->hCaptureEvent);
} else {
AuxDebugEx(1, DEBUGLINE "VideoCallback with NO instance data\r\n");
}
}
//
// 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.
UINT AVIVideoPrepare (LPCAPSTREAM lpcs)
{
MMRESULT mmr;
int ii;
// When performing MCI step capture, buffer array is not used
//
if (lpcs->sCapParms.fStepMCIDevice)
return 0;
// Open the video stream, setting the capture rate
//
mmr = videoStreamInit (lpcs->hVideoIn,
lpcs->sCapParms.dwRequestMicroSecPerFrame,
(DWORD_PTR) VideoCallback,
(DWORD_PTR) lpcs,
CALLBACK_FUNCTION);
if (mmr) {
dprintf("cannot open video device! Error is %d\n", mmr);
return IDS_CAP_VIDEO_OPEN_ERROR;
}
// Prepare (lock) the buffers, and give them to the device
//
for (ii = 0; ii < lpcs->iNumVideo; ++ii)
{
mmr = videoStreamPrepareHeader (lpcs->hVideoIn,
lpcs->alpVideoHdr[ii],
sizeof(VIDEOHDR));
if (mmr)
{
lpcs->iNumVideo = ii;
dprintf("**** could only prepare %d Video buffers!\n", lpcs->iNumVideo);
break;
}
mmr = videoStreamAddBuffer (lpcs->hVideoIn,
lpcs->alpVideoHdr[ii],
sizeof(VIDEOHDR));
if (mmr)
return IDS_CAP_VIDEO_ALLOC_ERROR;
AuxDebugEx(3, DEBUGLINE "Added video buffer %d (%8x)\r\n", ii, lpcs->alpVideoHdr[ii]);
}
return 0;
}
#endif // not chicago
//
// 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.
UINT AVIVideoUnPrepare (LPCAPSTREAM lpcs)
{
// 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
#if defined CHICAGO
vidxFreeHeaders (lpcs->hVideoIn);
ZeroMemory (lpcs->alpVideoHdr, sizeof(lpcs->alpVideoHdr));
#else
{
int ii;
#ifdef SINGLEHEADERBLOCK
LPCAPVIDEOHDR pcvhAll = (LPVOID)lpcs->alpVideoHdr[0];
#endif
for (ii = 0; ii < lpcs->iNumVideo; ++ii)
{
LPCAPVIDEOHDR pcvh = (LPVOID)lpcs->alpVideoHdr[ii];
if (pcvh)
{
if (pcvh->vh.dwFlags & VHDR_PREPARED)
videoStreamUnprepareHeader (lpcs->hVideoIn,
&pcvh->vh,
sizeof(VIDEOHDR));
if (pcvh->pAlloc) {
#ifdef ALLOW_HW_BUFFERS
if (pcvh->bHwBuffer)
{
dprintf("Freeing hardware buffer %d at address %8x", ii, pcvh->pAlloc);
videoStreamFreeBuffer (lpcs->hVideoIn, (LPVOID)pcvh->pAlloc);
}
else
#endif
{
dprintf("Freeing video buffer %d at address %8x", ii, pcvh->pAlloc);
FreeSectorAlignedMem (pcvh->pAlloc);
}
} else {
dprintf("NO buffer allocated for index %d", ii);
}
#ifndef SINGLEHEADERBLOCK
GlobalFreePtr(pcvh);
#endif
lpcs->alpVideoHdr[ii] = NULL;
} else {
dprintf("NO video header for index %d", ii);
}
}
#ifdef SINGLEHEADERBLOCK
// free the array of video headers
//
if (pcvhAll) {
GlobalFreePtr (pcvhAll);
}
#endif
}
#endif
// Shut down the video stream
videoStreamFini(lpcs->hVideoIn);
}
return 0;
}
/*
* AVI Fini - undo the mess that AVIInit did.
*
*/
void AVIFini(LPCAPSTREAM lpcs)
{
AuxDebugEx (2, "AVIFini(%08x)\r\n", lpcs);
if (lpcs->lpDropFrame) {
FreeSectorAlignedMem (lpcs->lpDropFrame), lpcs->lpDropFrame = NULL;
}
AVIVideoUnPrepare (lpcs); // Free the video device and buffers
AVIAudioUnPrepare (lpcs); // Free the audio device
AVIAudioFini (lpcs); // Free the audio buffers
if (lpcs->hCaptureEvent) {
CloseHandle (lpcs->hCaptureEvent), lpcs->hCaptureEvent = NULL;
}
if (lpcs->heSyncWrite) {
CloseHandle (lpcs->heSyncWrite), lpcs->heSyncWrite = NULL;
}
if (lpcs->hCompletionPort) {
CloseHandle (lpcs->hCompletionPort), lpcs->hCompletionPort = NULL;
}
if (hmodKernel) {
pfnCreateIoCompletionPort = NULL;
pfnGetQueuedCompletionStatus = NULL;
FreeLibrary(hmodKernel);
hmodKernel = 0;
}
AuxDebugEx (2, "AVIFini(...) exits\r\n");
}
//
// AVI Init
// This routine does all the non-File initalization for AVICapture.
// Returns: 0 on success, Error string value on failure.
//
UINT AVIInit (LPCAPSTREAM lpcs)
{
UINT wError = 0; // Success
LPBITMAPINFO lpBitsInfoOut; // Possibly compressed output format
// Allocate a DropFrame buffer
if (lpcs->lpDropFrame == NULL) {
assert (lpcs->dwBytesPerSector);
lpcs->lpDropFrame = AllocSectorAlignedMem (lpcs->dwBytesPerSector, lpcs->dwBytesPerSector);
}
/* No special video format given -- use the default */
#ifdef NEW_COMPMAN
if (lpcs->CompVars.hic == NULL)
lpBitsInfoOut = lpcs->lpBitsInfo;
else
lpBitsInfoOut = lpcs->CompVars.lpbiOut;
#else
lpBitsInfoOut = lpcs->lpBitsInfo;
#endif
// -------------------------------------------------------
// figure out buffer sizes
// -------------------------------------------------------
// Init all pointers to NULL
ZeroMemory (lpcs->alpVideoHdr, sizeof(lpcs->alpVideoHdr));
ZeroMemory (lpcs->alpWaveHdr, sizeof(lpcs->alpWaveHdr));
// -------------------------------------------------------
// Init Sound
// -------------------------------------------------------
if (lpcs->sCapParms.fCaptureAudio) {
if ((DWORD)(wError = AVIAudioInit (lpcs))) {
dprintf("can't init audio buffers!\n");
goto AVIInitFailed;
}
}
// -------------------------------------------------------
// Init Video
// -------------------------------------------------------
if ((DWORD)(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 ((DWORD)(wError = AVIAudioPrepare (lpcs))) {
dprintf("can't prepare audio buffers!\n");
goto AVIInitFailed;
}
}
// --------------------------------------------------------------
// Prepare video buffers (lock em down) and give them to the device
// --------------------------------------------------------------
if ((DWORD)(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;
}
// 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
// note: lpw and lpEnd are LPRIFF values... except the code is written
// to use them as pointers to an array of DWORD values. (yuk)
//
lpw = (LPBYTE)lpcs->lpInfoChunks; // always points at fcc
lpEnd = (LPBYTE)lpcs->lpInfoChunks + lpcs->cbInfoChunks;
while (lpw < lpEnd) {
cbSizeThis = ((DWORD UNALIGNED FAR *)lpw)[1];
cbSizeThis += cbSizeThis & 1; // force WORD (16 bit) alignment
// Point lpNext at the next RIFF block
lpNext = lpw + cbSizeThis + sizeof (DWORD) * 2;
// If this info chunk is the same as that passed in... we can delete the
// existing information
if ((*(DWORD UNALIGNED FAR *) lpw) == ckid) {
lpcs->cbInfoChunks -= cbSizeThis + sizeof (DWORD) * 2;
// could have coded: lpcs->cbInfoChunks -= lpNext - lpw;
// the next line should always be true...
if (lpNext <= lpEnd) {
if (lpEnd - lpNext)
CopyMemory (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 (16 bit) alignment
cbData += sizeof(RIFF); // add sizeof RIFF
if (lpcs->lpInfoChunks)
lp = GlobalReAllocPtr(lpcs->lpInfoChunks, lpcs->cbInfoChunks + cbData, GMEM_MOVEABLE);
else
lp = GlobalAllocPtr(GMEM_MOVEABLE, cbData);
if (!lp)
return FALSE;
// Save the pointer in our status block
lpcs->lpInfoChunks = lp;
// build RIFF chunk in block
//
((LPRIFF)(lp + lpcs->cbInfoChunks))->dwType = ckid;
((LPRIFF)(lp + lpcs->cbInfoChunks))->dwSize = lpcic->cbData;
CopyMemory (lp + lpcs->cbInfoChunks + sizeof(RIFF),
lpData,
cbData - sizeof(RIFF));
// Update the length of the info chunk
lpcs->cbInfoChunks += cbData;
return TRUE;
}
/*+ ProcessNextVideoBuffer
*
*-===============================================================*/
STATICFN BOOL _inline ProcessNextVideoBuffer (
LPCAPSTREAM lpcs,
BOOL fStopping,
LPUINT lpuError,
LPVIDEOHDR * plpvhDraw,
LPBOOL lpbPending)
{
LPVIDEOHDR lpvh;
*lpuError = 0;
*plpvhDraw = NULL;
*lpbPending = FALSE;
lpvh = lpcs->alpVideoHdr[lpcs->iNextVideo];
if (!(lpvh->dwFlags & VHDR_DONE)) {
return fStopping;
}
#if defined CHICAGO
{
LPTHKVIDEOHDR lptvh = (LPVOID)lpvh;
#ifdef JMK_HACK_CHECKHDR
if (IsBadWritePtr (lptvh, sizeof(*lptvh)) ||
HIWORD(lptvh->vh.lpData) != HIWORD(lptvh->p16Alloc))
{
OutputDebugStringA(DEBUGLINE "trouble with video hdr\r\n");
AuxDebugEx (0, DEBUGLINE "iNext=%d, ptvh=%X\r\n", lpcs->iNextVideo, lptvh);
AuxDebugDump (0, lptvh, sizeof(*lptvh));
AuxDebugEx (0, DEBUGLINE "alpVideoHdrs=%X\r\n", lpcs->alpVideoHdr);
AuxDebugDump (0, lpcs->alpVideoHdr, sizeof(lpcs->alpVideoHdr[0]) * 8);
INLINE_BREAK;
return TRUE;
}
#endif
// Swap the linear pointer back (was swapped in vidxAddBuffer)
//
lptvh->vh.lpData = (LPVOID)(ROUNDUPTOSECTORSIZE(lptvh->p32Buff, lpcs->dwBytesPerSector) + sizeof(RIFF));
}
#endif
if (lpvh->dwBytesUsed)
{
DWORD dwTime;
DWORD dwBytesUsed = lpvh->dwBytesUsed;
BOOL fKeyFrame = lpvh->dwFlags & VHDR_KEYFRAME;
LPVOID lpData = lpvh->lpData;
// get expected time for this frame in milliseconds
//
dwTime = MulDiv (lpcs->dwVideoChunkCount + 1,
lpcs->sCapParms.dwRequestMicroSecPerFrame,
1000);
#ifdef NEW_COMPMAN
//
// We are automatically compressing during capture, so
// compress the frame before we pass it on to be written
//
if (lpcs->CompVars.hic)
{
LPRIFF priff;
dwBytesUsed = 0;
lpData = ICSeqCompressFrame(&lpcs->CompVars, 0,
lpvh->lpData,
&fKeyFrame,
&dwBytesUsed);
priff = ((LPRIFF)lpData) -1;
priff->dwType = MAKEAVICKID(cktypeDIBbits, 0);
priff->dwSize = dwBytesUsed;
}
#endif // NEW_COMPMAN
// do video stream callback for this frame
//
if (lpcs->CallbackOnVideoStream)
lpcs->CallbackOnVideoStream (lpcs->hwnd, lpvh);
lpvh->dwFlags &= ~VHDR_DONE;
// if we are not capturing to disk, just increment
// the 'chunk count' (i.e. frame count?) and go on
// otherwise we want to queue the frame up to write
// here
//
if ( ! (lpcs->fCaptureFlags & CAP_fCapturingToDisk))
{
// Warning: Kludge to create frame chunk count when net capture
// follows.
++lpcs->dwVideoChunkCount;
}
else
{
int nAppendDummyFrames = 0;
// if the expected time for this frame is less than the
// timestamp for the frame. we may have dropped some frames
// before this frame.
//
if (lpcs->dwVideoChunkCount && (dwTime < lpvh->dwTimeCaptured))
{
int nDropCount;
BOOL bPending;
// calculate how many frames have been dropped.
// NOTE: this number may be zero if dwTimeCaptured is just
// a little bit late.
//
nDropCount = MulDiv(lpvh->dwTimeCaptured - dwTime,
1000,
lpcs->sCapParms.dwRequestMicroSecPerFrame);
#ifdef JMK_HACK_TIMERS
if (pTimerRiff)
pTimerRiff->vchd.dwDropFramesNotAppended += nDropCount;
#endif
// If any frames have been dropped, write them out before
// we get back to writing the current frame.
//
if (nDropCount > 0)
{
AuxDebugEx(2,"*****Adding %d to the dropcount\r\n", nDropCount);
lpcs->dwFramesDropped += nDropCount;
if (! AVIWriteDummyFrames (lpcs, nDropCount, lpuError, &bPending))
fStopping = TRUE;
}
}
#ifdef JMK_HACK_TIMERS
if (pTimerRiff) {
if (nTimerIndex == CLIPBOARDLOGSIZE)
nTimerIndex = 0;
// nTimerIndex will be OK if ((nTimerIndex < CLIPBOARDLOGSIZE) && pTimerStuff)
if (pTimerStuff)
{
pCurTimerStuff = &pTimerStuff[nTimerIndex];
++nTimerIndex;
pCurTimerStuff->nFramesAppended = 0;
pCurTimerStuff->nDummyFrames = (WORD)lpcs->dwFramesDropped;
pCurTimerStuff->dwFrameTickTime = dwTime;
pCurTimerStuff->dwFrameStampTime = lpvh->dwTimeCaptured;
pCurTimerStuff->dwVideoChunkCount = lpcs->dwVideoChunkCount;
pCurTimerStuff->dwTimeWritten = pcDeltaTicks(&pctWriteBase);
pCurTimerStuff->dwTimeToWrite = 0;
pCurTimerStuff->nVideoIndex = (WORD)lpcs->iNextVideo;
pCurTimerStuff->nAudioIndex = (WORD)lpcs->iNextWave;
}
} // fClipboardLogging
#endif // JMK_HACK_TIMERS
// look ahead for dummy frames and try to
// append them to the current frame
//
nAppendDummyFrames = 0;
#define LOOKAHEAD_FOR_DUMMYS 1
#ifdef LOOKAHEAD_FOR_DUMMYS
{
int iNext;
LPVIDEOHDR lpvhNext;
iNext = lpcs->iNextVideo+1;
if (iNext >= lpcs->iNumVideo)
iNext = 0;
// is the next frame done already? if so
// we can append any dropped frames to the end of
// this frame before we write it out
//
lpvhNext = lpcs->alpVideoHdr[iNext];
if (lpvhNext->dwFlags & VHDR_DONE)
{
// Recalculate the current time, which may have
// changed if dummy frames were inserted above
dwTime = MulDiv (lpcs->dwVideoChunkCount + 1,
lpcs->sCapParms.dwRequestMicroSecPerFrame,
1000);
nAppendDummyFrames =
MulDiv (lpvhNext->dwTimeCaptured - dwTime,
1000,
lpcs->sCapParms.dwRequestMicroSecPerFrame);
if ((--nAppendDummyFrames) < 0)
nAppendDummyFrames = 0;
else {
AuxDebugEx(3, DEBUGLINE "Appending %d dummy frames", nAppendDummyFrames);
}
AuxDebugEx(1,"*****Adding %d to the dropcount in lookahead mode\r\n", nAppendDummyFrames);
lpcs->dwFramesDropped += nAppendDummyFrames;
#ifdef JMK_HACK_TIMERS
if (pTimerRiff) {
pTimerRiff->vchd.dwDropFramesAppended += nAppendDummyFrames;
pCurTimerStuff->nFramesAppended = (WORD)nAppendDummyFrames;
}
#endif
}
}
#endif
if ( ! AVIWriteVideoFrame (lpcs,
lpData,
dwBytesUsed,
fKeyFrame,
lpcs->iNextVideo,
nAppendDummyFrames,
lpuError, lpbPending))
fStopping = TRUE;
#ifdef JMK_HACK_TIMERS
if (pCurTimerStuff)
{
pCurTimerStuff->dwTimeToWrite = pcDeltaTicks(&pctWriteBase);
pCurTimerStuff->bPending = (BOOL) *lpbPending;
}
#endif
}
}
// return lpvh to the caller so that the frame can be
// drawn (time permitting)
//
*plpvhDraw = lpvh;
// increment the next Video buffer pointer
//
if (++lpcs->iNextVideo >= lpcs->iNumVideo)
lpcs->iNextVideo = 0;
return fStopping;
}
/*+ ProcessAudioBuffers
*
*-===============================================================*/
STATICFN BOOL _inline ProcessAudioBuffers (
LPCAPSTREAM lpcs,
BOOL fStopping,
LPUINT lpuError)
{
int iLastWave;
UINT ii;
LPWAVEHDR lpwh;
*lpuError = 0;
assert (lpcs->sCapParms.fCaptureAudio);
// 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;
// process all done buffers, but no more than iNumAudio at one
// pass (to avoid getting stuck here forever)
//
for (ii = 0; ii < (UINT)lpcs->iNumAudio; ++ii)
{
BOOL bPending;
// if the next buffer is not done, break out of the loop
// and return to the caller
//
lpwh = lpcs->alpWaveHdr[lpcs->iNextWave];
if (!(lpwh->dwFlags & WHDR_DONE))
break;
lpwh->dwFlags &= ~WHDR_DONE;
// is there any data in the buffer ?
// if so, do wave stream callback, then write the
// buffer
//
bPending = FALSE;
if (lpwh->dwBytesRecorded)
{
if (lpcs->CallbackOnWaveStream)
lpcs->CallbackOnWaveStream (lpcs->hwnd, lpwh);
if ( ! (lpcs->fCaptureFlags & CAP_fCapturingToDisk))
{
lpcs->dwWaveChunkCount++;
lpcs->dwWaveBytes += lpwh->dwBytesRecorded;
}
else
{
// write the audio buffer, bPending will be true
// if the write will complete asynchronously
//
if ( ! AVIWriteAudio (lpcs, lpwh, lpcs->iNextWave,
lpuError, &bPending))
fStopping = TRUE;
}
}
// if we are not writing async, we can put the buffer
// back on the wave driver's queue now
//
if ( ! bPending)
{
lpwh->dwBytesRecorded = 0;
AuxDebugEx(3, DEBUGLINE "Calling waveInAddBuffer for address %8x", lpwh);
if (waveInAddBuffer(lpcs->hWaveIn, lpwh, sizeof(WAVEHDR)))
{
fStopping = TRUE;
*lpuError = IDS_CAP_WAVE_ADD_ERROR;
}
}
// increment the next wave buffer pointer
//
if (++lpcs->iNextWave >= lpcs->iNumAudio)
lpcs->iNextWave = 0;
}
return fStopping;
}
/*+ ProcessAsyncIOBuffers
*
*-===============================================================*/
STATICFN BOOL _inline ProcessAsyncIOBuffers (
LPCAPSTREAM lpcs,
BOOL fStopping,
LPUINT lpuError)
{
UINT ii;
struct _avi_async * lpah;
// if there are no async buffer headers, there is nothing to do!
//
*lpuError = 0;
assert (lpcs->pAsync);
//
// process all done buffers, stopping when there are no more outstanding
// iNextAsync can never go past iLastAsync.
//
while(lpcs->iNextAsync != lpcs->iLastAsync)
{
DWORD dwUsed;
// if this async header has never been used,
// we are done
//
lpah = &lpcs->pAsync[lpcs->iNextAsync];
assert (lpah->uType);
AuxDebugEx (2, DEBUGLINE "processing async io buffer %d off=%x\r\n",
lpcs->iNextAsync, lpah->ovl.Offset);
// if the next buffer is not done, or failed break
// out of the loop
//
// if the io on this block has already completed (because the IO
// completed out of order) queue it to the device without waiting
// otherwise get the next completion status. If a block has
// completed, and it is the block at the head of the async queue,
// then it can be passed straight back to the device queue. If the
// completed block is not the one we are expecting, we mark the IO
// as complete, then return. Thought..call GetQueuedCompletionStatus
// in a loop, until there are no more blocks pending. This way we
// might get to complete the block we want on this call to
// ProcessAsyncIOBuffers.
//
if (lpah->uType & ASYNCIOPENDING) {
DWORD dwWritten;
DWORD key;
LPOVERLAPPED povl;
BOOL fResult =
pfnGetQueuedCompletionStatus(lpcs->hCompletionPort,
&dwWritten,
&key,
&povl,
0);
if (fResult) {
// we dequeued a block. Did we dequeue the one we wanted?
((struct _avi_async *)povl)->uType &= ~ASYNCIOPENDING;
if ((PVOID)povl == (PVOID)lpah) {
// this is the one we wanted
// fall through and add back to the device queue
AuxDebugEx(2,"Dequeued the block we wanted at %8x\r\n", lpah);
} else {
// the io block completed out of order.
// Clear the io pending flag and return.
AuxDebugEx(1,"Dequeued out of order at %8x\r\n", povl->hEvent);
break;
}
} else {
if (povl) {
// a failed io operation
*lpuError = IDS_CAP_FILE_WRITE_ERROR;
AuxDebugEx(1, DEBUGLINE "A failed IO operation (GQCS)\r\n");
fStopping = TRUE;
} else {
// nothing completed
AuxDebugEx(3, DEBUGLINE "Nothing completed on call to GQCS\r\n");
break;
}
}
} else {
// IO is already complete for this block
}
// the buffer is done, so now we need to queue the wave/video
// buffer back to the wave/video driver
//
assert (!(lpah->uType & ASYNCIOPENDING));
switch (lpah->uType)
{
case ASYNC_BUF_VIDEO:
{
LPVIDEOHDR lpvh = lpcs->alpVideoHdr[lpah->uIndex];
#if defined CHICAGO
if (vidxAddBuffer(lpcs->hVideoIn, lpvh, sizeof (VIDEOHDR)))
#else
AuxDebugEx(3, DEBUGLINE "Queueing video buffer lpvh=%x (index %d)\r\n", lpvh, lpah->uIndex);
if (videoStreamAddBuffer(lpcs->hVideoIn, lpvh, sizeof (VIDEOHDR)))
#endif
{
fStopping = TRUE;
*lpuError = IDS_CAP_VIDEO_ADD_ERROR;
}
}
break;
case ASYNC_BUF_AUDIO:
{
LPWAVEHDR lpwh = lpcs->alpWaveHdr[lpah->uIndex];
lpwh->dwBytesRecorded = 0;
AuxDebugEx(3, DEBUGLINE "Queueing audio buffer lpwh=%x (index %d)\r\n", lpwh, lpah->uIndex);
if (waveInAddBuffer (lpcs->hWaveIn, lpwh, sizeof(WAVEHDR)))
{
fStopping = TRUE;
*lpuError = IDS_CAP_WAVE_ADD_ERROR;
}
}
break;
//case ASYNC_BUF_DROP:
//{
//}
//break;
}
// mark the overlapped header structure as vacant
lpah->uType = 0;
lpah->uIndex = 0;
// increment to the next async io buffer
//
if (++lpcs->iNextAsync >= lpcs->iNumAsync)
lpcs->iNextAsync = 0; // wrapped...
}
return fStopping;
}
/*+ ShowCompletionStatus
*
*-===============================================================*/
STATICFN void ShowCompletionStatus (
LPCAPSTREAM lpcs,
BOOL fCapturedOK)
{
// Notify if there was an error while recording
//
if ( ! fCapturedOK)
errorUpdateError (lpcs, IDS_CAP_RECORDING_ERROR);
// put up completion message on status line
//
if (lpcs->fCaptureFlags & CAP_fCapturingToDisk)
{
DWORD dw;
// The muldiv32 doesn't give 0 if numerator is zero
dw = 0;
if (lpcs->dwVideoChunkCount)
dw = muldiv32(lpcs->dwVideoChunkCount,1000000,lpcs->dwTimeElapsedMS);
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,
(UINT)(lpcs->dwTimeElapsedMS/1000),
(UINT)(lpcs->dwTimeElapsedMS%1000),
lpcs->dwVideoChunkCount,
lpcs->dwFramesDropped,
(UINT)(dw / 1000),
(UINT)(dw % 1000),
lpcs->dwWaveBytes,
(UINT) lpcs->lpWaveFormat->nSamplesPerSec / 1000,
(UINT) lpcs->lpWaveFormat->nSamplesPerSec % 1000);
}
else
{
// "Captured %d.%03d sec. %ld frames (%ld dropped) (%d.%03d fps)."
statusUpdateStatus(lpcs, IDS_CAP_STAT_VIDEOONLY,
(UINT)(lpcs->dwTimeElapsedMS/1000),
(UINT)(lpcs->dwTimeElapsedMS%1000),
lpcs->dwVideoChunkCount,
lpcs->dwFramesDropped,
(UINT)(dw / 1000),
(UINT)(dw % 1000));
}
} // endif capturing to disk (no warnings or errors if to net)
// if capture was successful, warn the user about various abnormal
// conditions.
//
if (fCapturedOK)
{
if (lpcs->dwVideoChunkCount == 0)
{
// No frames captured, warn user that interrupts are probably not enabled.
errorUpdateError (lpcs, IDS_CAP_NO_FRAME_CAP_ERROR);
}
else if (lpcs->sCapParms.fCaptureAudio && lpcs->dwWaveBytes == 0)
{
// No audio captured, warn user audio card is hosed
errorUpdateError (lpcs, IDS_CAP_NO_AUDIO_CAP_ERROR);
}
else if (lpcs->sCapParms.fCaptureAudio && lpcs->fAudioBreak)
{
// some of the audio was dropped
if(lpcs->CompVars.hic) {
errorUpdateError (lpcs, IDS_CAP_AUDIO_DROP_COMPERROR);
} else {
errorUpdateError (lpcs, IDS_CAP_AUDIO_DROP_ERROR);
}
}
else if (lpcs->fCaptureFlags & CAP_fCapturingToDisk)
{
DWORD dwPctDropped;
assert (lpcs->dwVideoChunkCount);
dwPctDropped = 100 * lpcs->dwFramesDropped / lpcs->dwVideoChunkCount;
//
// dropped > 10% (default) of the frames
//
if (dwPctDropped > lpcs->sCapParms.wPercentDropForError)
errorUpdateError (lpcs, IDS_CAP_STAT_FRAMESDROPPED,
lpcs->dwFramesDropped,
lpcs->dwVideoChunkCount,
(UINT)(muldiv32(lpcs->dwFramesDropped,10000,lpcs->dwVideoChunkCount)/100),
(UINT)(muldiv32(lpcs->dwFramesDropped,10000,lpcs->dwVideoChunkCount)%100)/10
);
}
}
}
/*
* 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 fCapturedOK = TRUE;
BOOL fStopping; // True when finishing capture
BOOL fStopped; // True if driver notified to stop
TCHAR ach[128];
TCHAR achMsg[128];
UINT wError; // Error String ID
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;
BOOL fTryToPaintAgain = FALSE;
HDC hdc;
HPALETTE hpalT;
HCURSOR hOldCursor;
RECT rcDrawRect;
CAPINFOCHUNK cic;
DWORD dwOldPrio;
BOOL bVideoWritePending;
LPVIDEOHDR lpvhDraw;
lpcs->fCaptureFlags |= CAP_fCapturingNow;
// we should Assert that CAP_fCapturingNow is already turned on
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);
goto EarlyExit;
}
statusUpdateStatus(lpcs, IDS_CAP_STAT_CAP_INIT);
// Try painting the DIB only if Live window
fTryToPaintAgain = fTryToPaint = lpcs->fLiveWindow;
if (fTryToPaint) {
hdc = GetDC(lpcs->hwnd);
SetWindowOrgEx(hdc, lpcs->ptScroll.x, lpcs->ptScroll.y, NULL);
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) {
DWORD dwTime;
// 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);
dwTime = lpcs->sCapParms.dwMCIStopTime - lpcs->sCapParms.dwMCIStartTime;
if (lpcs->sCapParms.fLimitEnabled)
dwTimeToStop = min (dwTime, dwTimeToStop);
else
dwTimeToStop = dwTime;
}
//
// never ever try to capture more than the index size!
//
if (lpcs->fCaptureFlags & CAP_fCapturingToDisk)
{
DWORD dwTime = MulDiv (lpcs->sCapParms.dwIndexSize,
lpcs->sCapParms.dwRequestMicroSecPerFrame,
1000l);
dwTimeToStop = min (dwTime, dwTimeToStop);
}
// if doing MCI capture, initialize MCI device. if init fails
// go straight to the exit code
//
if (lpcs->sCapParms.fMCIControl)
{
if ( ! MCIDeviceOpen (lpcs) ||
! MCIDeviceSetPosition (lpcs, lpcs->sCapParms.dwMCIStartTime))
{
fCapturedOK = FALSE;
errorUpdateError (lpcs, IDS_CAP_MCI_CONTROL_ERROR);
statusUpdateStatus(lpcs, 0); // Clear status
goto EarlyExit;
}
}
//
// If we're compressing while capturing, warm up the compressor
//
#ifdef NEW_COMPMAN
if (lpcs->CompVars.hic)
{
if ( ! ICSeqCompressFrameStart (&lpcs->CompVars, lpcs->lpBitsInfo))
{
// !!! We're in trouble here!
dprintf("ICSeqCompressFrameStart failed !!!\n");
errorUpdateError (lpcs, IDS_CAP_COMPRESSOR_ERROR);
goto EarlyExit;
}
// HACK WARNING !!!
// Kludge, offset the lpBitsOut ptr
// Compman allocates the compress buffer too large by
// 2048 + 16 so we will still have room
// By stepping on 8 bytes we give ourselves room for a RIFF header
//
((LPBYTE)lpcs->CompVars.lpBitsOut) += 8;
assert(lpcs->CompVars.lpbiOut != NULL);
}
#endif
// -------------------------------------------------------
// Open the output file
// -------------------------------------------------------
if (lpcs->fCaptureFlags & CAP_fCapturingToDisk) {
if (!CapFileInit(lpcs))
{
errorUpdateError (lpcs, IDS_CAP_FILE_OPEN_ERROR);
goto EarlyExit;
}
} else {
AuxDebugEx (3, DEBUGLINE "Setting dwBytesPerSector to %d\r\n",DEFAULT_BYTESPERSECTOR);
lpcs->dwBytesPerSector=DEFAULT_BYTESPERSECTOR;
}
#ifdef JMK_HACK_TIMERS
// Allocate memory for logging capture results to the clipboard if requested
if (GetProfileIntA ("Avicap32", "ClipboardLogging", FALSE))
{
AuxDebugEx (2, DEBUGLINE "ClipboardLogging Enabled\r\n");
InitPerformanceCounters();
pcBegin(), pctWriteBase = pc.base;
hMemTimers = GlobalAlloc(GHND | GMEM_ZEROINIT,
sizeof(struct _timerriff) +
sizeof(struct _timerstuff) * CLIPBOARDLOGSIZE);
if (hMemTimers && ((DWORD_PTR)(pTimerRiff = GlobalLock (hMemTimers))))
;
else if (hMemTimers)
{
GlobalFree(hMemTimers);
pTimerRiff = 0;
pTimerStuff = 0;
hMemTimers = 0;
}
nTimerIndex = 0;
nSleepCount = 0;
} // if ClipboardLogging
#endif // JMK_HACK_TIMERS
// Make sure the parent has been repainted
//
UpdateWindow(lpcs->hwnd);
//
// call AVIInit() to get all the capture memory we will need
//
wError = IDS_CAP_AVI_INIT_ERROR;
lpcs->hCaptureEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
if (lpcs->hCaptureEvent)
{
#ifdef CHICAGO
lpcs->hRing0CapEvt = OpenVxDHandle (lpcs->hCaptureEvent);
if ( ! lpcs->hRing0CapEvt)
CloseHandle (lpcs->hCaptureEvent), lpcs->hCaptureEvent = NULL;
else
#endif
wError = AVIInit(lpcs);
}
// if avifile init failed, cleanup and return error.
//
if (wError)
{
// Error in initalization - return
//
errorUpdateError (lpcs, wError);
AVIFini(lpcs);
AVIFileFini(lpcs, TRUE, TRUE);
statusUpdateStatus(lpcs, 0); // Clear status
goto EarlyExit;
}
// Click OK to capture string (must follow AVIInit)
//
LoadString(lpcs->hInst, IDS_CAP_SEQ_MSGSTART, ach, NUMELMS(ach));
wsprintf(achMsg, ach, (LPBYTE)lpcs->achFile);
// clear status
//
statusUpdateStatus(lpcs, 0);
// -------------------------------------------------------
// Ready to go, make the user click OK?
// -------------------------------------------------------
if (lpcs->sCapParms.fMakeUserHitOKToCapture && (lpcs->fCaptureFlags & CAP_fCapturingToDisk))
{
UINT idBtn;
idBtn = MessageBox (lpcs->hwnd, achMsg, TEXT(""),
MB_OKCANCEL | MB_ICONEXCLAMATION);
if (idBtn == IDCANCEL)
{
AVIFini(lpcs);
AVIFileFini (lpcs, TRUE, TRUE);
statusUpdateStatus (lpcs, 0);
goto EarlyExit;
}
}
// update the status, so the user knows how to stop
//
statusUpdateStatus(lpcs, IDS_CAP_SEQ_MSGSTOP);
UpdateWindow(lpcs->hwnd);
// this should be an ASSERT. After all, we turned the flag on at the
// top of the routine
//lpcs->fCaptureFlags |= CAP_fCapturingNow;
// query async key states to 'reset' them to current values
//
GetAsyncKeyState(lpcs->sCapParms.vKeyAbort);
GetAsyncKeyState(VK_ESCAPE);
GetAsyncKeyState(VK_LBUTTON);
GetAsyncKeyState(VK_RBUTTON);
// Insert the digitization time
// strings written to the file should be ascii, since this is
// an ascii file format.
//
//
// no point in pulling in the whole C runtime just to get a silly
// timestamp, so we just cook the system time into ascii right here.
//
{
SYSTEMTIME time;
// Note: both szDay and szMonth are explicitly null-terminated by virtue
// of being C strings ... "xxx"
static char szDay[] = "Sun\0Mon\0Tue\0Wed\0Thu\0Fri\0Sat";
#define DAYLENGTH (sizeof(szDay)/7)
static char szMonth[] = "Jan\0Feb\0Mar\0Apr\0May\0Jun\0Jul\0Aug\0Sep\0Oct\0Nov\0Dec";
#define MONTHLENGTH (sizeof(szMonth)/12)
char sz[30];
GetLocalTime (&time);
// note: GetLocalTime returns months in range 1-12
// returns days in range 0-6
//example: Fri Apr 29 8:25:12 1994
wsprintfA(sz, "%s %s %2d %2d:%02d:%02d %4d",
szDay + time.wDayOfWeek * DAYLENGTH,
szMonth-MONTHLENGTH + time.wMonth * MONTHLENGTH,
time.wDay, time.wHour, time.wMinute, time.wSecond, time.wYear);
cic.fccInfoID = mmioFOURCC ('I','D','I','T');
cic.lpData = sz;
cic.cbData = 25; // WARNING: this length is static.
SetInfoChunk (lpcs, &cic);
}
// -------------------------------------------------------
// Start MCI, Audio, and video streams
// -------------------------------------------------------
// 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->CallbackOnControl(lpcs->hwnd, CONTROLCALLBACK_PREROLL))
{
AVIFini(lpcs);
AVIFileFini(lpcs, TRUE, TRUE);
statusUpdateStatus(lpcs, 0);
goto EarlyExit;
}
dwOldPrio = GetThreadPriority(GetCurrentThread());
if (dwOldPrio != THREAD_PRIORITY_HIGHEST)
SetThreadPriority (GetCurrentThread(), THREAD_PRIORITY_HIGHEST);
#ifdef JMK_HACK_TIMERS
if (pTimerRiff)
{
UINT ii;
pTimerRiff->fccRIFF = RIFFTYPE('RIFF'); //MAKEFOURCC('R','I','F','F');
pTimerRiff->cbTotal = sizeof(struct _timerriff) - 8 +
sizeof(struct _timerstuff) * CLIPBOARDLOGSIZE;
pTimerRiff->fccJMKD = RIFFTYPE('JMKD'); //MAKEFOURCC('J','M','K','D');
pTimerRiff->fccVCHD = RIFFTYPE('VCHD'); //MAKEFOURCC('V','C','H','D');
pTimerRiff->cbVCHD = sizeof(struct _vchd);
pTimerRiff->vchd.nPrio = GetThreadPriority(GetCurrentThread());
pTimerRiff->vchd.bmih = lpcs->lpBitsInfo->bmiHeader;
pTimerRiff->vchd.cap = lpcs->sCapParms;
pTimerRiff->vchd.dwDropFramesAppended = 0;
pTimerRiff->vchd.dwDropFramesNotAppended = 0;
pTimerRiff->vchd.dwTimerFrequency = pcGetTickRate();
for (ii = 0; ii < NUMELMS(pTimerRiff->vchd.atvh); ++ii)
{
if (lpcs->alpVideoHdr[ii])
{
struct _thkvideohdr * ptvh = (LPVOID)lpcs->alpVideoHdr[ii];
#ifndef CHICAGO
assert (sizeof(CAPVIDEOHDR) == sizeof(*ptvh));
#endif
pTimerRiff->vchd.atvh[ii] = *ptvh;
pTimerRiff->vchd.nMaxVideoBuffers = ii;
}
}
pTimerRiff->fccChunk = RIFFTYPE('VCAP'); //MAKEFOURCC('V','C','A','P');
pTimerRiff->cbChunk = pTimerRiff->cbTotal - sizeof(*pTimerRiff);
pTimerStuff = (LPVOID)(pTimerRiff + 1);
pCurTimerStuff = &pTimerStuff[0];
} // fClipboardLogging
#endif // JMK_HACK_TIMERS
// make sure that the fat is loaded before we begin capturing
//
AVIPreloadFat (lpcs);
// start the MCI device
//
if (lpcs->sCapParms.fMCIControl)
MCIDevicePlay (lpcs);
dwTimeStarted = timeGetTime();
// start audio & video streams
//
if (lpcs->sCapParms.fCaptureAudio)
waveInStart(lpcs->hWaveIn);
videoStreamStart(lpcs->hVideoIn);
// -------------------------------------------------------
// MAIN CAPTURE LOOP
// -------------------------------------------------------
fCapturedOK=TRUE;
fStopping = FALSE; // TRUE when we need to stop
fStopped = FALSE; // TRUE if drivers notified we have stopped
lpcs->dwTimeElapsedMS = 0;
assert (lpcs->iNextVideo == 0);
if (lpcs->sCapParms.fCaptureAudio) {
assert (lpcs->iNextWave == 0);
lpWaveHdr = lpcs->alpWaveHdr[lpcs->iNextWave];
// lpWaveHdr is only interesting when we capture audio
}
lpVidHdr = lpcs->alpVideoHdr[lpcs->iNextVideo];
DPF("Start of main capture loop");
for (;;)
{
// The INTEL driver uses the GetError message to
// process buffers, so call it often...
// FIX JAYBO videoStreamGetError (lpcs->hVideoIn, &dwStreamError, &dwDriverDropCount);
// if there are no buffers to process, we either wait
// or leave the loop forever (depending on whether we expect
// more buffers to be done in the future)
//
if (!(lpVidHdr->dwFlags & VHDR_DONE) &&
!(lpcs->sCapParms.fCaptureAudio
&& (lpWaveHdr->dwFlags & WHDR_DONE)))
{
if (fStopped)
break;
#ifdef JMK_HACK_TIMERS
if (pCurTimerStuff)
{
pCurTimerStuff->nSleepCount = ++nSleepCount;
pCurTimerStuff->dwSleepBegin = pcGetTicks();
}
#endif
AuxDebugEx(2,DEBUGLINE "***** Waiting for something interesting to happen while capturing\r\n");
WaitForSingleObject (lpcs->hCaptureEvent, 300);
#ifdef JMK_HACK_TIMERS
if (pCurTimerStuff)
{
pCurTimerStuff->dwSleepEnd = pcGetTicks();
}
#endif
}
// What time is it?
lpcs->dwTimeElapsedMS = timeGetTime() - dwTimeStarted;
// -------------------------------------------------------
// Is video buffer ready to be written?
// -------------------------------------------------------
if ((DWORD)(fStopping = ProcessNextVideoBuffer (lpcs,
fStopping,
&wError,
&lpvhDraw, // captured frame to draw if time permits
&bVideoWritePending))) // TRUE if Write pending on lpvhDraw
{
AuxDebugEx (1, DEBUGLINE "ProcessVideo stopping\r\n");
if (wError)
{
AuxDebugEx (1, DEBUGLINE "ProcessVideo return error %d\r\n", wError);
errorUpdateError (lpcs, wError);
fCapturedOK = FALSE;
break;
}
}
lpVidHdr = lpcs->alpVideoHdr[lpcs->iNextVideo];
// if there is still more time, (or at least every 100 frames)
// show status if we're not ending the capture
//
if (!fStopping &&
(lpcs->fCaptureFlags & CAP_fCapturingToDisk) &&
(!(lpVidHdr->dwFlags & VHDR_DONE) ||
(lpcs->dwVideoChunkCount && (lpcs->dwVideoChunkCount % 100 == 0))))
{
// Captured %ld frames (Dropped %ld) %d.%03d sec. Hit Escape to Stop
//
statusUpdateStatus(lpcs, IDS_CAP_STAT_VIDEOCURRENT,
lpcs->dwVideoChunkCount,
lpcs->dwFramesDropped,
(UINT)(lpcs->dwTimeElapsedMS/1000),
(UINT)(lpcs->dwTimeElapsedMS%1000));
}
// If the yield callback returns FALSE, abort
//
if (lpcs->CallbackOnYield && !lpcs->CallbackOnYield (lpcs->hwnd))
fStopping = TRUE;
#if 0 // this is a 16 bit ism??
// 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);
}
}
}
#endif
// Outside routine is handling when to stop
// The CONTROLCALLBACK_CAPTURING indicates we're asking when to stop
//
if (lpcs->CallbackOnControl &&
!lpcs->CallbackOnControl (lpcs->hwnd, CONTROLCALLBACK_CAPTURING))
fStopping = TRUE;
// -------------------------------------------------------
// Is audio buffer ready to be written?
// -------------------------------------------------------
if (lpcs->sCapParms.fCaptureAudio) {
if ((DWORD)(fStopping = ProcessAudioBuffers (lpcs, fStopping, &wError)))
{
AuxDebugEx (1, DEBUGLINE "ProcessAudio stopping\r\n");
if (wError)
{
AuxDebugEx (1, DEBUGLINE "ProcessAudio return error %d\r\n", wError);
errorUpdateError (lpcs, wError);
fCapturedOK = FALSE;
break;
}
}
lpWaveHdr = lpcs->alpWaveHdr[lpcs->iNextWave];
}
// if we are not writing the frame async, we can put the video buffer
// back on the video driver's queue now
//
if (lpvhDraw)
{
// if the next video header is not ready yet, and
// we have no outstanding io buffers (in async mode) draw
// the current one
//
if ( !(lpVidHdr->dwFlags & VHDR_DONE) &&
(!lpcs->pAsync ||
(lpcs->iNextAsync+2 >= lpcs->iLastAsync)) &&
lpvhDraw->dwBytesUsed)
{
AuxDebugEx (4, DEBUGLINE "time enough to draw!\r\n");
if (fTryToPaintAgain &&
lpcs->dwVideoChunkCount &&
lpvhDraw->dwFlags & VHDR_KEYFRAME)
{
fTryToPaintAgain = DrawDibDraw(lpcs->hdd, hdc,
0, 0,
rcDrawRect.right - rcDrawRect.left,
rcDrawRect.bottom - rcDrawRect.top,
/*lpcs->dxBits, lpcs->dyBits, */
(LPBITMAPINFOHEADER)lpcs->lpBitsInfo,
lpvhDraw->lpData, 0, 0, -1, -1,
DDF_SAME_HDC | DDF_SAME_DIB | DDF_SAME_SIZE);
}
}
// if there is not a write pending for the draw frame
// put it back into the video drivers queue now
//
if ( ! bVideoWritePending)
{
AuxDebugEx(3, DEBUGLINE "Queueing video buffer, lpvh=%8x", lpvhDraw);
// return the emptied buffer to the que
//
#if defined CHICAGO
if (vidxAddBuffer(lpcs->hVideoIn, lpvhDraw, sizeof (VIDEOHDR)))
#else
if (videoStreamAddBuffer(lpcs->hVideoIn, lpvhDraw, sizeof (VIDEOHDR)))
#endif
{
AuxDebugEx (2, DEBUGLINE "Failed to Queue Video buffer %08x\r\n", lpvhDraw);
errorUpdateError (lpcs, IDS_CAP_VIDEO_ADD_ERROR);
fCapturedOK = FALSE;
fStopping = TRUE;
break;
}
}
}
// ------------------------------------------------------------
// Any completed I/O buffers?
// ------------------------------------------------------------
if (lpcs->pAsync)
if ((DWORD)(fStopping = ProcessAsyncIOBuffers (lpcs, fStopping, &wError)))
{
if (wError)
{
errorUpdateError (lpcs, wError);
fCapturedOK = FALSE;
break;
}
}
// -------------------------------------------------------
// is there any reason to stop?
// -------------------------------------------------------
if (!fStopping)
{
if (lpcs->sCapParms.vKeyAbort &&
(GetAsyncKeyState(lpcs->sCapParms.vKeyAbort & 0x00ff) & 0x0001))
{
BOOL 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 && (GetAsyncKeyState(VK_LBUTTON) & 0x0001))
fStopping = TRUE; // User aborts
if (lpcs->sCapParms.fAbortRightMouse && (GetAsyncKeyState(VK_RBUTTON) & 0x0001))
fStopping = TRUE; // User aborts
if ((lpcs->fCaptureFlags & CAP_fAbortCapture) || (lpcs->fCaptureFlags & CAP_fStopCapture))
fStopping = TRUE; // Somebody above wants us to quit
if (lpcs->dwTimeElapsedMS > dwTimeToStop)
fStopping = TRUE; // all done
#ifdef DEBUG
if (fStopping)
AuxDebugEx (1, DEBUGLINE "user stop\r\n");
#endif
}
// -------------------------------------------------------
// Tell all the devices to stop
// -------------------------------------------------------
if (fStopping)
{
if ( ! 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");
// Force cursor back to hourglass
//
SetCursor(lpcs->hWaitCursor);
}
// "Finished capture, now writing frame %ld"
//
if (fCapturedOK)
statusUpdateStatus(lpcs, IDS_CAP_STAT_CAP_FINI, lpcs->dwVideoChunkCount);
else
{
statusUpdateStatus(lpcs, IDS_CAP_RECORDING_ERROR2);
break;
}
// Wait for all the async IO to complete ??
//
}
// -------------------------------------------------------
// END OF MAIN CAPTURE LOOP
// -------------------------------------------------------
}
DPF("End of main capture loop");
// eat any keys that have been pressed
//
while(GetKey(FALSE))
;
// flush stuff to disk, close everything etc.
//
AVIFini(lpcs);
AVIFileFini(lpcs, TRUE, !fCapturedOK);
// This is the corrected capture duration, based on audio samples
lpcs->dwTimeElapsedMS = lpcs->dwActualMicroSecPerFrame *
lpcs->dwVideoChunkCount / 1000;
// update the status line with information about the completed
// capture, or with erroor information
//
ShowCompletionStatus (lpcs, fCapturedOK);
EarlyExit:
//
// If we we're compressing while capturing, close it down
//
#ifdef NEW_COMPMAN
if (lpcs->CompVars.hic) {
// Kludge, reset the lpBitsOut pointer
if (lpcs->CompVars.lpBitsOut)
((LPBYTE) lpcs->CompVars.lpBitsOut) -= 8;
ICSeqCompressFrameEnd(&lpcs->CompVars);
}
#endif
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);
SetThreadPriority (GetCurrentThread(), dwOldPrio);
SetCursor(hOldCursor);
lpcs->fCapFileExists = (lpcs->dwReturn == DV_ERR_OK);
lpcs->fCaptureFlags &= ~CAP_fCapturingNow;
statusUpdateStatus(lpcs, IDS_CAP_END); // Always the last message
#ifdef JMK_HACK_TIMERS
if (pTimerRiff)
{
UINT ii;
UINT kk;
LPSTR psz;
HGLOBAL hMem;
kk = (lpcs->dwVideoChunkCount >= CLIPBOARDLOGSIZE) ?
CLIPBOARDLOGSIZE : nTimerIndex;
hMem = GlobalAlloc (GHND, (16 * 5 + 2) * kk + 80);
if (hMem && ((DWORD_PTR)(psz = GlobalLock (hMem))))
{
pTimerRiff->vchd.dwFramesCaptured = lpcs->dwVideoChunkCount;
pTimerRiff->vchd.dwFramesDropped = lpcs->dwFramesDropped;
pTimerRiff->cbTotal = sizeof(struct _timerriff) - 8 +
sizeof(struct _timerstuff) * nTimerIndex;
pTimerRiff->cbChunk = pTimerRiff->cbTotal - sizeof(*pTimerRiff);
lstrcpyA(psz, "Slot#, VideoIndex, ExpectedTime, DriverTime, AccumulatedDummyFrames, CurrentAppendedDummies");
for (ii = 0; ii < kk; ++ii)
{
psz += lstrlenA(psz);
wsprintfA(psz, "\r\n%d, %ld, %ld, %ld, %d, %d",
ii,
pTimerStuff[ii].dwVideoChunkCount,
pTimerStuff[ii].dwFrameTickTime,
pTimerStuff[ii].dwFrameStampTime,
pTimerStuff[ii].nDummyFrames,
pTimerStuff[ii].nFramesAppended
);
}
GlobalUnlock (hMem);
GlobalUnlock (hMemTimers);
if (OpenClipboard (lpcs->hwnd))
{
EmptyClipboard ();
SetClipboardData (CF_RIFF, hMemTimers);
SetClipboardData (CF_TEXT, hMem);
CloseClipboard ();
}
else
{
GlobalFree (hMem);
GlobalFree (hMemTimers);
}
}
else
{
// Failed to allocate or lock hMem. Cleanup.
//
if (hMem)
GlobalFree(hMem);
// Free off the timer block. (We have not set the
// clipboard data.)
//
if (hMemTimers)
{
GlobalUnlock(hMemTimers);
GlobalFree(hMemTimers);
}
}
hMemTimers = NULL;
pTimerRiff = NULL;
pTimerStuff = NULL;
pCurTimerStuff = NULL;
}
#endif
return;
}
// Returns TRUE if the capture task was created, or
// capture completed OK.
BOOL AVICapture (LPCAPSTREAM lpcs)
{
CAPINFOCHUNK cic;
void (WINAPI _LOADDS * pfnCapture) (LPCAPSTREAM lpcs);
if (lpcs->fCaptureFlags & CAP_fCapturingNow) {
AuxDebugEx(4, DEBUGLINE "rejecting capture as previous capture still running\r\n");
return IDS_CAP_VIDEO_OPEN_ERROR;
}
// if there is a previous capture thread, wait for it to finish and
// clean it up
// -it has set fCapturingNow to FALSE, so it will end 'soon' !
if (lpcs->hThreadCapture) {
AuxDebugEx(4, DEBUGLINE "Starting capture while previous capture thread still active\r\n");
WaitForSingleObject(lpcs->hThreadCapture, INFINITE);
CloseHandle(lpcs->hThreadCapture);
lpcs->hThreadCapture = NULL;
}
// Turn off the STOP and ABORT capture bits
lpcs->fCaptureFlags &= ~(CAP_fStopCapture | CAP_fAbortCapture);
lpcs->dwReturn = 0;
#if DONT_CLEAR_SMPTE_JAYBO
// Prior to Win95, we always cleared out old SMPTE chunks,
// but since Adobe may have created a chunk manually, don't
// zap existing chunks.
cic.fccInfoID = mmioFOURCC ('I','S','M','T');
cic.lpData = NULL;
cic.cbData = 0;
SetInfoChunk (lpcs, &cic);
#endif
// And get ready to write a SMPTE info chunk
if (lpcs->sCapParms.fMCIControl) {
// create SMPTE string
CHAR szSMPTE[40]; // must write ansi
TimeMSToSMPTE (lpcs->sCapParms.dwMCIStartTime, szSMPTE);
cic.lpData = szSMPTE;
cic.cbData = lstrlenA(szSMPTE) + 1;
cic.fccInfoID = mmioFOURCC ('I','S','M','T');
SetInfoChunk (lpcs, &cic);
}
// set pfnCapture to point to the capture function of choice.
// Use an MCI device to do step capture capture???
// assume No MCI device, just a normal streaming capture
//
pfnCapture = AVICapture1;
if (lpcs->sCapParms.fStepMCIDevice && lpcs->sCapParms.fMCIControl)
pfnCapture = MCIStepCapture;
// if the fYield flag is true, create a thread to do the
// capture loop. otherwise do the capture loop inline
//
if (lpcs->sCapParms.fYield)
{
DWORD tid;
lpcs->fCaptureFlags |= CAP_fCapturingNow;
// future operations on this thread are now locked out.
// we must turn this flag off if the thread creation fails
lpcs->hThreadCapture = CreateThread (NULL,
0,
(LPTHREAD_START_ROUTINE) pfnCapture,
lpcs,
0,
&tid);
// if thread creation failed, turn off the capturing flag
//
if ( ! lpcs->hThreadCapture) {
AuxDebugEx(1,"Failed to create capture thread");
lpcs->fCaptureFlags &= ~CAP_fCapturingNow;
}
return (lpcs->hThreadCapture != NULL);
}
else
{
pfnCapture (lpcs);
return (0 == lpcs->dwReturn);
}
}