997 lines
34 KiB
C
997 lines
34 KiB
C
|
/****************************************************************************
|
|||
|
*
|
|||
|
* capmci.c
|
|||
|
*
|
|||
|
* Control of MCI devices during capture.
|
|||
|
*
|
|||
|
* 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 <stdlib.h>
|
|||
|
#include <ctype.h>
|
|||
|
#include <string.h>
|
|||
|
#include <msvideo.h>
|
|||
|
#include <drawdib.h>
|
|||
|
#include <mmddk.h>
|
|||
|
|
|||
|
#include "avicap.h"
|
|||
|
#include "avicapi.h"
|
|||
|
|
|||
|
#ifdef _DEBUG
|
|||
|
#define DSTATUS(lpcs, sz) statusUpdateStatus(lpcs, IDS_CAP_INFO, (LPSTR) sz)
|
|||
|
#else
|
|||
|
#define DSTATUS(lpcs, sz)
|
|||
|
#endif
|
|||
|
|
|||
|
DWORD SendDriverFormat (LPCAPSTREAM lpcs, LPBITMAPINFOHEADER lpbih, DWORD dwInfoHeaderSize);
|
|||
|
|
|||
|
/*--------------------------------------------------------------+
|
|||
|
| TimeMSToHMSString() - change milliseconds into SMPTE time |
|
|||
|
+--------------------------------------------------------------*/
|
|||
|
void FAR PASCAL TimeMSToSMPTE (DWORD dwMS, LPSTR lpTime)
|
|||
|
{
|
|||
|
DWORD dwTotalSecs;
|
|||
|
LONG lHundredths;
|
|||
|
WORD wSecs;
|
|||
|
WORD wMins;
|
|||
|
WORD wHours;
|
|||
|
|
|||
|
/* convert to number of seconds */
|
|||
|
dwTotalSecs = dwMS / 1000;
|
|||
|
|
|||
|
/* keep the remainder part */
|
|||
|
lHundredths = (dwMS - (dwTotalSecs * 1000)) / 10;
|
|||
|
|
|||
|
/* break down into other components */
|
|||
|
wHours = (WORD)(dwTotalSecs / 3600); // get # Hours
|
|||
|
dwTotalSecs -= (wHours * 3600);
|
|||
|
|
|||
|
wMins = (WORD)(dwTotalSecs / 60); // get # Mins
|
|||
|
dwTotalSecs -= (wMins * 60);
|
|||
|
|
|||
|
wSecs = (WORD)dwTotalSecs; // what's left is # seconds
|
|||
|
|
|||
|
/* build the string */
|
|||
|
/* KLUDGE, force hundredths to SMPTE approximation of PAL frames */
|
|||
|
wsprintf((char far *)lpTime, "%02u:%02u:%02u:%02lu", wHours, wMins,
|
|||
|
wSecs, (lHundredths * 25) / 100);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/*--------------------------------------------------------------+
|
|||
|
| START OF MCI CONTROL SECTION |
|
|||
|
+--------------------------------------------------------------*/
|
|||
|
|
|||
|
/*
|
|||
|
* CountMCIDevicesByType
|
|||
|
* Returns a count of the number of VCR or Videodisc
|
|||
|
* devices that MCI claims to know about.
|
|||
|
*/
|
|||
|
|
|||
|
int CountMCIDevicesByType ( WORD wType )
|
|||
|
{
|
|||
|
int nTotal;
|
|||
|
DWORD dwCount;
|
|||
|
MCI_SYSINFO_PARMS mciSIP;
|
|||
|
|
|||
|
mciSIP.dwCallback = NULL;
|
|||
|
mciSIP.lpstrReturn = (LPSTR) (LPVOID) &dwCount;
|
|||
|
mciSIP.dwRetSize = sizeof (dwCount);
|
|||
|
|
|||
|
mciSIP.wDeviceType = wType;
|
|||
|
|
|||
|
if (!mciSendCommand (NULL, MCI_SYSINFO, MCI_SYSINFO_QUANTITY,
|
|||
|
(DWORD) (LPVOID) &mciSIP))
|
|||
|
nTotal = (int) *( (LPDWORD) mciSIP.lpstrReturn);
|
|||
|
|
|||
|
return nTotal;
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
* MCIDeviceClose
|
|||
|
* This routine closes the open MCI device.
|
|||
|
*/
|
|||
|
|
|||
|
void MCIDeviceClose (LPCAPSTREAM lpcs)
|
|||
|
{
|
|||
|
mciSendString( "close mciframes", NULL, 0, NULL );
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
* MCIDeviceOpen
|
|||
|
* This routine opens the mci device for use, and sets the
|
|||
|
* time format to milliseconds.
|
|||
|
* Return FALSE on error;
|
|||
|
*/
|
|||
|
|
|||
|
BOOL MCIDeviceOpen (LPCAPSTREAM lpcs)
|
|||
|
{
|
|||
|
char ach[160];
|
|||
|
|
|||
|
wsprintf( ach, "open %s shareable alias mciframes",
|
|||
|
(LPSTR) lpcs-> achMCIDevice);
|
|||
|
lpcs-> dwMCIError = mciSendString( ach, NULL, 0, NULL );
|
|||
|
if( lpcs-> dwMCIError ) {
|
|||
|
DPF (" MCI Error, open %s shareable alias mciframes", lpcs-> achMCIDevice);
|
|||
|
goto err_return;
|
|||
|
}
|
|||
|
lpcs-> dwMCIError = mciSendString( "set mciframes time format milliseconds",
|
|||
|
NULL, 0, NULL );
|
|||
|
if( lpcs-> dwMCIError ) {
|
|||
|
DPF (" MCI Error, set mciframes time format milliseconds");
|
|||
|
goto err_close;
|
|||
|
}
|
|||
|
return ( TRUE );
|
|||
|
|
|||
|
err_close:
|
|||
|
MCIDeviceClose (lpcs);
|
|||
|
err_return:
|
|||
|
return ( FALSE );
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/*
|
|||
|
* MCIDeviceGetPosition
|
|||
|
* Stores the current device position in milliseconds in lpdwPos.
|
|||
|
* Returns TRUE on success, FALSE if error.
|
|||
|
*/
|
|||
|
BOOL FAR PASCAL MCIDeviceGetPosition (LPCAPSTREAM lpcs, LPDWORD lpdwPos)
|
|||
|
{
|
|||
|
char ach[80];
|
|||
|
LPSTR p;
|
|||
|
LONG lv;
|
|||
|
|
|||
|
lpcs-> dwMCIError = mciSendString( "status mciframes position wait",
|
|||
|
ach, sizeof(ach), NULL );
|
|||
|
if( lpcs-> dwMCIError ) {
|
|||
|
DPF (" MCI Error, status mciframes position wait");
|
|||
|
*lpdwPos = 0L;
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
p = ach;
|
|||
|
|
|||
|
while (*p == ' ') p++;
|
|||
|
for (lv = 0; *p >= '0' && *p <= '9'; p++)
|
|||
|
lv = (10 * lv) + (*p - '0');
|
|||
|
*lpdwPos = lv;
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
* MCIDeviceSetPosition
|
|||
|
* Sets the current device position in milliseconds.
|
|||
|
* Returns TRUE on success, FALSE if error.
|
|||
|
*/
|
|||
|
BOOL FAR PASCAL MCIDeviceSetPosition (LPCAPSTREAM lpcs, DWORD dwPos)
|
|||
|
{
|
|||
|
char achCommand[40];
|
|||
|
char ach[80];
|
|||
|
|
|||
|
lpcs-> dwMCIError = mciSendString( "pause mciframes wait", ach, sizeof(ach), NULL );
|
|||
|
if (lpcs-> dwMCIError) {
|
|||
|
DPF (" MCI Error, pause mciframes wait");
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
wsprintf(achCommand, "seek mciframes to %ld wait", dwPos);
|
|||
|
lpcs-> dwMCIError = mciSendString( achCommand, ach, sizeof(ach), NULL );
|
|||
|
if (lpcs-> dwMCIError)
|
|||
|
DPF (" MCI Error, seek mciframes to %ld wait", dwPos);
|
|||
|
return ( lpcs-> dwMCIError == 0 ? TRUE : FALSE );
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/*
|
|||
|
* MCIDevicePlay
|
|||
|
* Start playing the current MCI device from the current position
|
|||
|
* Returns TRUE on success, FALSE if error.
|
|||
|
*/
|
|||
|
BOOL FAR PASCAL MCIDevicePlay (LPCAPSTREAM lpcs)
|
|||
|
{
|
|||
|
char ach[80];
|
|||
|
|
|||
|
lpcs-> dwMCIError = mciSendString( "play mciframes", ach, sizeof(ach), NULL );
|
|||
|
if (lpcs-> dwMCIError)
|
|||
|
DPF (" MCI Error, play mciframes");
|
|||
|
return ( lpcs-> dwMCIError == 0 ? TRUE : FALSE );
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
* MCIDevicePause
|
|||
|
* Pauses the current MCI device at the current position
|
|||
|
* Returns TRUE on success, FALSE if error.
|
|||
|
*/
|
|||
|
BOOL FAR PASCAL MCIDevicePause (LPCAPSTREAM lpcs)
|
|||
|
{
|
|||
|
char ach[80];
|
|||
|
|
|||
|
lpcs-> dwMCIError = mciSendString( "pause mciframes wait", ach, sizeof(ach), NULL );
|
|||
|
if (lpcs-> dwMCIError)
|
|||
|
DPF (" MCI Error, pause mciframes wait");
|
|||
|
return ( lpcs-> dwMCIError == 0 ? TRUE : FALSE );
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
* MCIDeviceStop
|
|||
|
* Stops the current MCI device
|
|||
|
* Returns TRUE on success, FALSE if error.
|
|||
|
*/
|
|||
|
BOOL FAR PASCAL MCIDeviceStop (LPCAPSTREAM lpcs)
|
|||
|
{
|
|||
|
char ach[80];
|
|||
|
|
|||
|
lpcs-> dwMCIError = mciSendString( "stop mciframes wait", ach, sizeof(ach), NULL );
|
|||
|
if (lpcs-> dwMCIError)
|
|||
|
DPF (" MCI Error, stop mciframes wait");
|
|||
|
return ( lpcs-> dwMCIError == 0 ? TRUE : FALSE );
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
* MCIDeviceStep
|
|||
|
* Step the current MCI at the current position
|
|||
|
* Returns TRUE on success, FALSE if error.
|
|||
|
*/
|
|||
|
BOOL FAR PASCAL MCIDeviceStep (LPCAPSTREAM lpcs, BOOL fForward)
|
|||
|
{
|
|||
|
char ach[80];
|
|||
|
|
|||
|
lpcs-> dwMCIError = mciSendString( fForward ? "step mciframes wait" :
|
|||
|
"step mciframes reverse wait", ach, sizeof(ach), NULL );
|
|||
|
if (lpcs-> dwMCIError)
|
|||
|
DPF (" MCI Error, step mciframes wait");
|
|||
|
return ( lpcs-> dwMCIError == 0 ? TRUE : FALSE );
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
* MCIDeviceFreeze
|
|||
|
* freeze the current frame
|
|||
|
* Returns TRUE on success, FALSE if error.
|
|||
|
*/
|
|||
|
BOOL FAR PASCAL MCIDeviceFreeze(LPCAPSTREAM lpcs, BOOL fFreeze)
|
|||
|
{
|
|||
|
lpcs-> dwMCIError = mciSendString( fFreeze ? "freeze mciframes wait" :
|
|||
|
"unfreeze mciframes wait", NULL, 0, NULL);
|
|||
|
if (lpcs-> dwMCIError)
|
|||
|
DPF (" MCI Error, freeze mciframes wait");
|
|||
|
return ( lpcs-> dwMCIError == 0 ? TRUE : FALSE );
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/*
|
|||
|
* MCIStepCapture
|
|||
|
* Main routine for performing MCI step capture.
|
|||
|
*
|
|||
|
*/
|
|||
|
void FAR PASCAL _loadds MCIStepCapture (LPCAPSTREAM lpcs)
|
|||
|
{
|
|||
|
BOOL fOK = TRUE;
|
|||
|
BOOL fT;
|
|||
|
BOOL fKey;
|
|||
|
BOOL fStopping; // True when finishing capture
|
|||
|
DWORD dw;
|
|||
|
WORD w;
|
|||
|
WORD wError; // Error String ID
|
|||
|
LPVIDEOHDR lpVidHdr;
|
|||
|
LPWAVEHDR lpWaveHdr;
|
|||
|
DWORD dwTimeToStop; // Lesser of MCI capture time or frame limit
|
|||
|
BOOL fTryToPaint = FALSE;
|
|||
|
HDC hdc;
|
|||
|
HPALETTE hpalT;
|
|||
|
RECT rcDrawRect;
|
|||
|
LONG lSize;
|
|||
|
|
|||
|
statusUpdateStatus(lpcs, IDS_CAP_BEGIN); // Always the first message
|
|||
|
|
|||
|
// Verify capture parameters
|
|||
|
if ((!lpcs->sCapParms.fMCIControl) ||
|
|||
|
(!lpcs->sCapParms.fStepMCIDevice))
|
|||
|
goto EarlyExit;
|
|||
|
|
|||
|
lpcs->MCICaptureState = CAPMCI_STATE_Uninitialized;
|
|||
|
|
|||
|
lpcs-> fCapturingNow = TRUE;
|
|||
|
lpcs-> fStepCapturingNow = TRUE;
|
|||
|
lpcs-> dwReturn = DV_ERR_OK;
|
|||
|
|
|||
|
// 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);
|
|||
|
}
|
|||
|
|
|||
|
fOK = FALSE; // Assume the worst
|
|||
|
if (MCIDeviceOpen (lpcs)) {
|
|||
|
if (MCIDeviceSetPosition (lpcs, lpcs->sCapParms.dwMCIStartTime))
|
|||
|
if (MCIDeviceStep (lpcs, TRUE))
|
|||
|
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;
|
|||
|
}
|
|||
|
|
|||
|
// -------------------------------------------------------
|
|||
|
// Spatial and temporal averaging
|
|||
|
// -------------------------------------------------------
|
|||
|
|
|||
|
// Frame Averaging, capture the same frame multiple times...
|
|||
|
lpcs->lpia = NULL;
|
|||
|
if (lpcs->sCapParms.wStepCaptureAverageFrames == 0)
|
|||
|
lpcs->sCapParms.wStepCaptureAverageFrames = 1;
|
|||
|
|
|||
|
// Only allow averaging if an RGB format
|
|||
|
if (lpcs->lpBitsInfo->bmiHeader.biCompression != BI_RGB)
|
|||
|
lpcs->sCapParms.wStepCaptureAverageFrames = 1;
|
|||
|
|
|||
|
// 2x Scaling
|
|||
|
lpcs->lpbmih2x = NULL;
|
|||
|
lpcs->VidHdr2x = lpcs->VidHdr; // Init the 2x copy
|
|||
|
|
|||
|
if (lpcs->sCapParms.fStepCaptureAt2x &&
|
|||
|
lpcs->lpBitsInfo->bmiHeader.biCompression == BI_RGB) {
|
|||
|
lpcs->VidHdr2x.lpData = NULL;
|
|||
|
lpcs->lpbmih2x = (LPBITMAPINFOHEADER) GlobalAllocPtr (GHND,
|
|||
|
sizeof (BITMAPINFOHEADER) +
|
|||
|
256 * sizeof (RGBQUAD));
|
|||
|
_fmemcpy (lpcs->lpbmih2x, lpcs->lpBitsInfo, sizeof (BITMAPINFOHEADER) +
|
|||
|
256 * sizeof (RGBQUAD));
|
|||
|
|
|||
|
// Try to force the driver into 2x mode
|
|||
|
lpcs->lpbmih2x->biHeight *= 2;
|
|||
|
lpcs->lpbmih2x->biWidth *= 2;
|
|||
|
lpcs->lpbmih2x->biSizeImage *= 4;
|
|||
|
if (!SendDriverFormat (lpcs, lpcs->lpbmih2x, sizeof (BITMAPINFOHEADER))) {
|
|||
|
// Success, allocate new bitspace
|
|||
|
lpcs->VidHdr2x.lpData = GlobalAllocPtr (GHND,
|
|||
|
lpcs->lpbmih2x->biSizeImage);
|
|||
|
lpcs->VidHdr2x.dwBufferLength = lpcs->lpbmih2x->biSizeImage;
|
|||
|
}
|
|||
|
|
|||
|
// Something went wrong, no memory, or driver failed request
|
|||
|
// so revert back to original settings
|
|||
|
if (!lpcs->VidHdr2x.lpData) {
|
|||
|
SendDriverFormat (lpcs, (LPBITMAPINFOHEADER) lpcs->lpBitsInfo,
|
|||
|
sizeof (BITMAPINFOHEADER));
|
|||
|
lpcs->sCapParms.fStepCaptureAt2x = FALSE;
|
|||
|
lpcs->VidHdr2x = lpcs->VidHdr; // Back to the original settings
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
lpcs->sCapParms.fStepCaptureAt2x = FALSE;
|
|||
|
|
|||
|
DPF (" StepCaptureAt2x = %d\r\n", (int) lpcs->sCapParms.fStepCaptureAt2x);
|
|||
|
|
|||
|
//
|
|||
|
// 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);
|
|||
|
|
|||
|
//
|
|||
|
// AVIInit will allocate sound buffers, but not video buffers
|
|||
|
// when performing step capture.
|
|||
|
//
|
|||
|
|
|||
|
wError = AVIInit(lpcs);
|
|||
|
|
|||
|
if (wError) {
|
|||
|
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;
|
|||
|
}
|
|||
|
|
|||
|
/* update the status, so the user knows how to stop */
|
|||
|
statusUpdateStatus(lpcs, IDS_CAP_SEQ_MSGSTOP);
|
|||
|
UpdateWindow(lpcs->hwnd);
|
|||
|
|
|||
|
|
|||
|
if (lpcs->sCapParms.fStepCaptureAt2x || (lpcs->sCapParms.wStepCaptureAverageFrames != 1)) {
|
|||
|
LPIAVERAGE FAR * lppia = (LPIAVERAGE FAR *) &lpcs->lpia;
|
|||
|
|
|||
|
statusUpdateStatus (lpcs, IDS_CAP_STAT_PALETTE_BUILD);
|
|||
|
if (!iaverageInit (lppia, lpcs->lpBitsInfo, lpcs->hPalCurrent)) {
|
|||
|
lpcs-> dwReturn = IDS_CAP_OUTOFMEM;
|
|||
|
goto CompressFrameFailure;
|
|||
|
}
|
|||
|
statusUpdateStatus(lpcs, NULL);
|
|||
|
}
|
|||
|
DPF (" Averaging %d frames\r\n", lpcs->sCapParms.wStepCaptureAverageFrames);
|
|||
|
|
|||
|
GetAsyncKeyState(lpcs->sCapParms.vKeyAbort);
|
|||
|
GetAsyncKeyState(VK_ESCAPE);
|
|||
|
GetAsyncKeyState(VK_LBUTTON);
|
|||
|
GetAsyncKeyState(VK_RBUTTON);
|
|||
|
|
|||
|
|
|||
|
// -------------------------------------------------------
|
|||
|
// MAIN VIDEO CAPTURE LOOP
|
|||
|
// -------------------------------------------------------
|
|||
|
|
|||
|
fOK=TRUE; // Set FALSE on write errors
|
|||
|
fStopping = FALSE; // TRUE when we need to stop
|
|||
|
|
|||
|
lpVidHdr = &lpcs->VidHdr;
|
|||
|
lpWaveHdr = lpcs->alpWaveHdr[lpcs->iNextWave];
|
|||
|
|
|||
|
lpcs->MCICaptureState = CAPMCI_STATE_Initialized;
|
|||
|
lpcs->dwTimeElapsedMS = 0;
|
|||
|
|
|||
|
// Move back to the starting position
|
|||
|
MCIDeviceSetPosition (lpcs, lpcs->sCapParms.dwMCIStartTime);
|
|||
|
MCIDevicePause (lpcs);
|
|||
|
|
|||
|
// Where are we *really*
|
|||
|
MCIDeviceGetPosition (lpcs, &lpcs->dwMCIActualStartMS);
|
|||
|
|
|||
|
// freeze video
|
|||
|
MCIDeviceFreeze(lpcs, TRUE);
|
|||
|
|
|||
|
while (lpcs->MCICaptureState != CAPMCI_STATE_AllFini) {
|
|||
|
|
|||
|
// -------------------------------------------------------
|
|||
|
// is there any reason to stop or change states
|
|||
|
// -------------------------------------------------------
|
|||
|
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 0
|
|||
|
// Ignore Left mouse on MCI Capture!!!
|
|||
|
if (lpcs->sCapParms.fAbortLeftMouse)
|
|||
|
if (GetAsyncKeyState(VK_LBUTTON) & 0x0001)
|
|||
|
fStopping = TRUE; // User aborts
|
|||
|
#endif
|
|||
|
|
|||
|
if (lpcs->sCapParms.fAbortRightMouse)
|
|||
|
if (GetAsyncKeyState(VK_RBUTTON) & 0x0001)
|
|||
|
fStopping = TRUE; // User aborts
|
|||
|
if (lpcs-> fAbortCapture) {
|
|||
|
fStopping = TRUE; // Somebody above wants us to quit
|
|||
|
}
|
|||
|
if (lpcs-> dwTimeElapsedMS > dwTimeToStop)
|
|||
|
fStopping = TRUE; // all done
|
|||
|
|
|||
|
|
|||
|
// -------------------------------------------------------
|
|||
|
// State machine
|
|||
|
// -------------------------------------------------------
|
|||
|
switch (lpcs-> MCICaptureState) {
|
|||
|
|
|||
|
case CAPMCI_STATE_Initialized:
|
|||
|
// Begin video step capture
|
|||
|
DSTATUS(lpcs, "MCIState: Initialized");
|
|||
|
lpcs->MCICaptureState = CAPMCI_STATE_StartVideo;
|
|||
|
break;
|
|||
|
|
|||
|
case CAPMCI_STATE_StartVideo:
|
|||
|
// Begin video step capture
|
|||
|
lpcs->dwTimeElapsedMS = 0;
|
|||
|
lpcs->MCICaptureState = CAPMCI_STATE_CapturingVideo;
|
|||
|
break;
|
|||
|
|
|||
|
case CAPMCI_STATE_CapturingVideo:
|
|||
|
// In the state of capturing video
|
|||
|
if (lpcs-> fStopCapture || lpcs-> fAbortCapture)
|
|||
|
fStopping = TRUE;
|
|||
|
|
|||
|
if (fStopping) {
|
|||
|
MCIDeviceGetPosition (lpcs, &lpcs->dwMCIActualEndMS);
|
|||
|
MCIDevicePause (lpcs);
|
|||
|
|
|||
|
DSTATUS(lpcs, "MCIState: StoppingVideo");
|
|||
|
|
|||
|
if (fOK && !lpcs-> fAbortCapture)
|
|||
|
lpcs->MCICaptureState = CAPMCI_STATE_VideoFini;
|
|||
|
else
|
|||
|
lpcs->MCICaptureState = CAPMCI_STATE_AllFini;
|
|||
|
|
|||
|
lpcs-> fStopCapture = FALSE;
|
|||
|
lpcs-> fAbortCapture = FALSE;
|
|||
|
fStopping = FALSE;
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
case CAPMCI_STATE_VideoFini:
|
|||
|
// Wait for all buffers to be returned from the driver
|
|||
|
// Then move on to audio capture
|
|||
|
lpcs->MCICaptureState = CAPMCI_STATE_StartAudio;
|
|||
|
DSTATUS(lpcs, "MCIState: VideoFini");
|
|||
|
break;
|
|||
|
|
|||
|
case CAPMCI_STATE_StartAudio:
|
|||
|
// If no audio, go to AllFini state
|
|||
|
if (!lpcs->sCapParms.fCaptureAudio || !fOK) {
|
|||
|
lpcs->MCICaptureState = CAPMCI_STATE_AllFini;
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
// Move back to the starting position
|
|||
|
MCIDeviceSetPosition (lpcs, lpcs->dwMCIActualStartMS);
|
|||
|
MCIDeviceGetPosition (lpcs, &lpcs->dwMCICurrentMS);
|
|||
|
DSTATUS(lpcs, "MCIState: StartAudio");
|
|||
|
MCIDevicePlay (lpcs);
|
|||
|
waveInStart(lpcs->hWaveIn);
|
|||
|
lpcs->MCICaptureState = CAPMCI_STATE_CapturingAudio;
|
|||
|
lpcs->dwTimeElapsedMS = 0;
|
|||
|
fStopping = FALSE;
|
|||
|
break;
|
|||
|
|
|||
|
case CAPMCI_STATE_CapturingAudio:
|
|||
|
// In the state of capturing audio
|
|||
|
if (lpcs-> fStopCapture || lpcs-> fAbortCapture)
|
|||
|
fStopping = TRUE;
|
|||
|
|
|||
|
MCIDeviceGetPosition (lpcs, &lpcs->dwMCICurrentMS);
|
|||
|
if (lpcs->dwMCICurrentMS + 100 > lpcs->dwMCIActualEndMS)
|
|||
|
fStopping = TRUE;
|
|||
|
if (fStopping) {
|
|||
|
waveInStop(lpcs->hWaveIn);
|
|||
|
MCIDevicePause (lpcs);
|
|||
|
waveInReset(lpcs->hWaveIn);
|
|||
|
lpcs->MCICaptureState = CAPMCI_STATE_AudioFini;
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
case CAPMCI_STATE_AudioFini:
|
|||
|
// While more audio buffers to process
|
|||
|
if (lpWaveHdr-> dwFlags & WHDR_DONE)
|
|||
|
break;
|
|||
|
lpcs->MCICaptureState = CAPMCI_STATE_AllFini;
|
|||
|
break;
|
|||
|
|
|||
|
case CAPMCI_STATE_AllFini:
|
|||
|
DSTATUS(lpcs, "MCIState: AllFini");
|
|||
|
if (fOK)
|
|||
|
statusUpdateStatus(lpcs, IDS_CAP_STAT_CAP_FINI, lpcs->dwVideoChunkCount);
|
|||
|
else
|
|||
|
statusUpdateStatus(lpcs, IDS_CAP_RECORDING_ERROR2);
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
// -------------------------------------------------------
|
|||
|
// If we are in the video capture phase
|
|||
|
// -------------------------------------------------------
|
|||
|
|
|||
|
if (lpcs->MCICaptureState == CAPMCI_STATE_CapturingVideo) {
|
|||
|
|
|||
|
// if averaging...
|
|||
|
if (lpcs-> lpia) {
|
|||
|
int j;
|
|||
|
|
|||
|
iaverageZero (lpcs-> lpia);
|
|||
|
|
|||
|
// sum together a bunch of frames
|
|||
|
for (j = 0; j < (int)lpcs->sCapParms.wStepCaptureAverageFrames; j++) {
|
|||
|
|
|||
|
videoFrame( lpcs-> hVideoIn, &lpcs-> VidHdr2x);
|
|||
|
|
|||
|
// Shrink by 2x??
|
|||
|
if (lpcs-> sCapParms.fStepCaptureAt2x) {
|
|||
|
CrunchDIB(
|
|||
|
lpcs-> lpia, // image averaging structure
|
|||
|
(LPBITMAPINFOHEADER) lpcs-> lpbmih2x, // BITMAPINFO src
|
|||
|
(LPVOID) lpcs-> VidHdr2x.lpData, // input bits
|
|||
|
(LPBITMAPINFOHEADER) lpcs->lpBitsInfo, // BITMAPINFO dst
|
|||
|
(LPVOID) lpcs->VidHdr.lpData); // output bits
|
|||
|
}
|
|||
|
iaverageSum (lpcs-> lpia, lpcs->lpBits);
|
|||
|
}
|
|||
|
iaverageDivide (lpcs-> lpia, lpcs->lpBits);
|
|||
|
}
|
|||
|
// otherwise, not averaging, just get a frame
|
|||
|
else {
|
|||
|
videoFrame( lpcs-> hVideoIn, &lpcs->VidHdr);
|
|||
|
}
|
|||
|
|
|||
|
if (lpcs->CallbackOnVideoFrame)
|
|||
|
(*(lpcs->CallbackOnVideoFrame)) (lpcs->hwnd, &lpcs->VidHdr);
|
|||
|
|
|||
|
// Update the display
|
|||
|
InvalidateRect(lpcs->hwnd, NULL, TRUE);
|
|||
|
UpdateWindow(lpcs->hwnd);
|
|||
|
|
|||
|
if (lpcs-> fCapturingToDisk) {
|
|||
|
if (!SingleFrameWrite (lpcs, lpVidHdr, &fKey, &lSize)) {
|
|||
|
fOK = FALSE;
|
|||
|
fStopping = TRUE;
|
|||
|
// "ERROR: Could not write to file."
|
|||
|
errorUpdateError(lpcs, IDS_CAP_FILE_WRITE_ERROR);
|
|||
|
}
|
|||
|
else {
|
|||
|
if (!IndexVideo(lpcs, lSize, fKey))
|
|||
|
fStopping = TRUE;
|
|||
|
}
|
|||
|
} // endif fCapturingToDisk
|
|||
|
// Warning: Kludge to create frame chunk count when net capture
|
|||
|
// follows.
|
|||
|
else
|
|||
|
lpcs->dwVideoChunkCount++;
|
|||
|
|
|||
|
// 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)) {
|
|||
|
|
|||
|
// "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
|
|||
|
|
|||
|
// Move the MCI source to the next capture point
|
|||
|
// unfreeze video
|
|||
|
MCIDeviceFreeze(lpcs, FALSE);
|
|||
|
for (;;) {
|
|||
|
MCIDeviceGetPosition (lpcs, &lpcs->dwMCICurrentMS);
|
|||
|
if (lpcs->dwMCICurrentMS > ((DWORD) (lpcs->dwMCIActualStartMS +
|
|||
|
muldiv32 (lpcs->dwVideoChunkCount,
|
|||
|
lpcs->sCapParms.dwRequestMicroSecPerFrame,
|
|||
|
1000L))))
|
|||
|
break;
|
|||
|
MCIDeviceStep (lpcs, TRUE);
|
|||
|
}
|
|||
|
// freeze video
|
|||
|
MCIDeviceFreeze(lpcs, TRUE);
|
|||
|
lpcs-> dwTimeElapsedMS =
|
|||
|
lpcs->dwMCICurrentMS - lpcs->dwMCIActualStartMS;
|
|||
|
|
|||
|
/* return the emptied buffer to the que */
|
|||
|
lpVidHdr->dwFlags &= ~VHDR_DONE;
|
|||
|
}
|
|||
|
|
|||
|
if (lpcs-> CallbackOnYield) {
|
|||
|
// If the yield callback returns FALSE, abort
|
|||
|
if (!((*(lpcs->CallbackOnYield)) (lpcs->hwnd)))
|
|||
|
fStopping = TRUE;
|
|||
|
}
|
|||
|
|
|||
|
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);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// -------------------------------------------------------
|
|||
|
// Is audio buffer ready to be written?
|
|||
|
// -------------------------------------------------------
|
|||
|
if (lpcs->sCapParms.fCaptureAudio &&
|
|||
|
(lpcs-> MCICaptureState == CAPMCI_STATE_CapturingAudio ||
|
|||
|
lpcs-> MCICaptureState == CAPMCI_STATE_StartAudio ||
|
|||
|
lpcs-> MCICaptureState == CAPMCI_STATE_AudioFini)) {
|
|||
|
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
|
|||
|
} // end of forever
|
|||
|
|
|||
|
CompressFrameFailure:
|
|||
|
|
|||
|
iaverageFini (lpcs->lpia);
|
|||
|
|
|||
|
// Switch back to the normal format
|
|||
|
if (lpcs->sCapParms.fStepCaptureAt2x) {
|
|||
|
SendDriverFormat (lpcs, (LPBITMAPINFOHEADER) lpcs->lpBitsInfo,
|
|||
|
sizeof (BITMAPINFOHEADER));
|
|||
|
GlobalFreePtr (lpcs->VidHdr2x.lpData);
|
|||
|
lpcs->VidHdr2x.lpData = NULL;
|
|||
|
}
|
|||
|
|
|||
|
// And free the 2x memory
|
|||
|
if (lpcs->lpbmih2x) {
|
|||
|
GlobalFreePtr (lpcs->lpbmih2x);
|
|||
|
lpcs->lpbmih2x = NULL;
|
|||
|
}
|
|||
|
|
|||
|
// -------------------------------------------------------
|
|||
|
// END OF MAIN CAPTURE LOOP
|
|||
|
// -------------------------------------------------------
|
|||
|
|
|||
|
lpcs-> dwTimeElapsedMS = lpcs-> dwMCIActualEndMS - lpcs->dwMCIActualStartMS;
|
|||
|
|
|||
|
/* eat any keys that have been pressed */
|
|||
|
while(GetKey(FALSE))
|
|||
|
;
|
|||
|
|
|||
|
AVIFini(lpcs); // does the Reset, and frees all buffers
|
|||
|
AVIFileFini(lpcs, FALSE /* fWroteJunkChunks */, FALSE /* fAbort */);
|
|||
|
|
|||
|
|
|||
|
/* 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 (%d 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 (%d 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) {
|
|||
|
|
|||
|
// "%d 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);
|
|||
|
|
|||
|
lpcs->fCapFileExists = (lpcs-> dwReturn == DV_ERR_OK);
|
|||
|
lpcs->fCapturingNow = FALSE;
|
|||
|
lpcs-> fStepCapturingNow = FALSE;
|
|||
|
|
|||
|
statusUpdateStatus(lpcs, IDS_CAP_END); // Always the last message
|
|||
|
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|