AMCAP.CPP

/************************************************************************** 
*
* THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
* KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
* PURPOSE.
*
* Copyright (c) 1993 - 1997 Microsoft Corporation. All Rights Reserved.
*
**************************************************************************/

#include <streams.h>
#include <mmreg.h>
#include <msacm.h>
#include "fcntl.h"
#include "io.h"
#include "stdio.h"
#include "amcap.h"
#include "status.h"

HINSTANCE ghInstApp;
HACCEL ghAccel;
HFONT ghfontApp;
TEXTMETRIC gtm;
TCHAR gszAppName[]=TEXT("AMCAP");
HWND ghwndApp, ghwndStatus;

// forgive me for using global variables
struct _capstuff {
char szCaptureFile[_MAX_PATH];
WORD wCapFileSize; // size in Meg
ICaptureGraphBuilder *pBuilder;
IVideoWindow *pVW;
IMediaEventEx *pME;
IAMDroppedFrames *pDF;
IAMVideoCompression *pVC;
IAMVfwCaptureDialogs *pDlg;
IAMStreamConfig *pASC; // for audio cap
IAMStreamConfig *pVSC; // for video cap
IBaseFilter *pRender;
IBaseFilter *pVCap, *pACap;
IGraphBuilder *pFg;
IFileSinkFilter *pSink;
IConfigAviMux *pConfigAviMux;
int iMasterStream;
BOOL fCaptureGraphBuilt;
BOOL fPreviewGraphBuilt;
BOOL fCapturing;
BOOL fPreviewing;
BOOL fCapAudio;
int iVideoDevice;
int iAudioDevice;
double FrameRate;
BOOL fWantPreview;
long lCapStartTime;
long lCapStopTime;
char achFriendlyName[120];
BOOL fUseTimeLimit;
DWORD dwTimeLimit;
int iFormatDialogPos;
int iSourceDialogPos;
int iDisplayDialogPos;
int iVCapDialogPos;
int iVCrossbarDialogPos;
int iTVTunerDialogPos;
int iACapDialogPos;
int iACrossbarDialogPos;
int iTVAudioDialogPos;
int iVCapCapturePinDialogPos;
int iVCapPreviewPinDialogPos;
int iACapCapturePinDialogPos;
} gcap;

typedef LONG (PASCAL *LPWNDPROC)(HWND, UINT, WPARAM, LPARAM); // pointer to a window procedure

/*----------------------------------------------------------------------------*\
\*----------------------------------------------------------------------------*/
LONG WINAPI AppWndProc(HWND hwnd, UINT uiMessage, WPARAM wParam, LPARAM lParam);
LONG PASCAL AppCommand(HWND hwnd, unsigned msg, WPARAM wParam, LPARAM lParam);
BOOL CALLBACK AboutDlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
int ErrMsg (LPTSTR sz,...);
BOOL SetCaptureFile(HWND hWnd);
BOOL SaveCaptureFile(HWND hWnd);
BOOL AllocCaptureFile(HWND hWnd);
int DoDialog(HWND hwndParent, int DialogID, DLGPROC fnDialog, long lParam);
int FAR PASCAL AllocCapFileProc(HWND hDlg, UINT Message, UINT wParam, LONG lParam);
int FAR PASCAL FrameRateProc(HWND hDlg, UINT Message, UINT wParam, LONG lParam);
int FAR PASCAL TimeLimitProc(HWND hDlg, UINT Message, UINT wParam, LONG lParam);
int FAR PASCAL PressAKeyProc(HWND hDlg, UINT Message, UINT wParam, LONG lParam);
void TearDownGraph(void);
BOOL BuildCaptureGraph();
BOOL BuildPreviewGraph();
void UpdateStatus(BOOL fAllStats);
void AddDevicesToMenu();
void ChooseDevices(int idV, int idA);
void ChooseFrameRate();
BOOL InitCapFilters();
void FreeCapFilters();
BOOL StopPreview();
BOOL StartPreview();
BOOL StopCapture();
DWORDLONG GetSize(LPCSTR ach);
void MakeMenuOptions();
/*----------------------------------------------------------------------------*\
\*----------------------------------------------------------------------------*/

// set our app's caption to be our app name followed by the capture file name
//
void SetAppCaption()
{
char ach[_MAX_PATH + 80];
lstrcpy(ach, gszAppName);
if (gcap.szCaptureFile[0] != 0) {
lstrcat(ach, " - ");
lstrcat(ach, gcap.szCaptureFile);
}
SetWindowText(ghwndApp, ach);
}


/*----------------------------------------------------------------------------*\
| AppInit( hInst, hPrev) |
| |
| Description: |
| This is called when the application is first loaded into |
| memory. It performs all initialization that doesn't need to be done |
| once per instance. |
| |
| Arguments: |
| hInstance instance handle of current instance |
| hPrev instance handle of previous instance |
| |
| Returns: |
| TRUE if successful, FALSE if not |
| |
\*----------------------------------------------------------------------------*/
BOOL AppInit(HINSTANCE hInst, HINSTANCE hPrev, int sw,LPSTR szCmdLine)
{
WNDCLASS cls;
HDC hdc;

const DWORD dwExStyle = 0;

CoInitialize(NULL);
DbgInitialise(hInst);

/* Save instance handle for DialogBoxs */
ghInstApp = hInst;

ghAccel = LoadAccelerators(hInst, MAKEINTATOM(ID_APP));

if (!hPrev) {
/*
* Register a class for the main application window
*/
cls.hCursor = LoadCursor(NULL,IDC_ARROW);
cls.hIcon = LoadIcon(hInst, TEXT("AMCapIcon"));
cls.lpszMenuName = MAKEINTATOM(ID_APP);
cls.lpszClassName = MAKEINTATOM(ID_APP);
cls.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
cls.hInstance = hInst;
cls.style = CS_BYTEALIGNCLIENT | CS_VREDRAW | CS_HREDRAW | CS_DBLCLKS;
cls.lpfnWndProc = (LPWNDPROC)AppWndProc;
cls.cbWndExtra = 0;
cls.cbClsExtra = 0;

if (!RegisterClass(&cls))
return FALSE;
}

// Is this necessary?
ghfontApp = (HFONT)GetStockObject(ANSI_VAR_FONT);
hdc = GetDC(NULL);
SelectObject(hdc, ghfontApp);
GetTextMetrics(hdc, &gtm);
ReleaseDC(NULL, hdc);

ghwndApp=CreateWindowEx(dwExStyle,
MAKEINTATOM(ID_APP), // Class name
gszAppName, // Caption
// Style bits
WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
CW_USEDEFAULT, 0, // Position
320,300, // Size
(HWND)NULL, // Parent window (no parent)
(HMENU)NULL, // use class menu
hInst, // handle to window instance
(LPSTR)NULL // no params to pass on
);

// create the status bar
statusInit(hInst, hPrev);
ghwndStatus = CreateWindowEx(
0,
szStatusClass,
NULL,
WS_CHILD|WS_BORDER|WS_VISIBLE|WS_CLIPSIBLINGS,
0, 0,
0, 0,
ghwndApp,
NULL,
hInst,
NULL);
if (ghwndStatus == NULL) {
return(FALSE);
}
ShowWindow(ghwndApp,sw);

// get the capture file name from win.ini
GetProfileString("annie", "CaptureFile", "", gcap.szCaptureFile,
sizeof(gcap.szCaptureFile));

// get which devices to use from win.ini
gcap.iVideoDevice = -1; // force update
gcap.iAudioDevice = -1; // force update
int idV = GetProfileInt("annie", "VideoDevice", 0);
int idA = GetProfileInt("annie", "AudioDevice", 0);
AddDevicesToMenu(); // list all capture devices in the system as menu items

// do we want audio?
gcap.fCapAudio = GetProfileInt("annie", "CaptureAudio", TRUE);
// do we want preview?
gcap.fWantPreview = GetProfileInt("annie", "WantPreview", FALSE);
// which stream should be the master? NONE(-1) means nothing special happens
// AUDIO(1) means the video frame rate is changed before written out to keep
// the movie in sync when the audio and video capture crystals are not the
// same (and therefore drift out of sync after a few minutes). VIDEO(0)
// means the audio sample rate is changed before written out
gcap.iMasterStream = GetProfileInt("annie", "MasterStream", -1);


// get the frame rate from win.ini before making the graph
int units_per_frame = GetProfileInt("annie", "FrameRate", 666667); // 15fps
gcap.FrameRate = 10000000. / units_per_frame;
gcap.FrameRate = (int)(gcap.FrameRate * 100) / 100.;
// reasonable default
if (gcap.FrameRate <= 0.)
gcap.FrameRate = 15.0;

// instantiate the capture filters we need to do the menu items
// this will start previewing, if wanted
ChooseDevices(idV, idA); // make these the official devices we're using
InitCapFilters(); // creates the capture filters we will use
ChooseDevices(idV, idA); // puts video capture device name in title bar

SetAppCaption();
return TRUE;
}

/*----------------------------------------------------------------------------*\
| WinMain( hInst, hPrev, lpszCmdLine, cmdShow ) |
| |
| Description: |
| The main procedure for the App. After initializing, it just goes |
| into a message-processing loop until it gets a WM_QUIT message |
| (meaning the app was closed). |
| |
| Arguments: |
| hInst instance handle of this instance of the app |
| hPrev instance handle of previous instance, NULL if first |
| szCmdLine ->null-terminated command line |
| cmdShow specifies how the window is initially displayed |
| |
| Returns: |
| The exit code as specified in the WM_QUIT message. |
| |
\*----------------------------------------------------------------------------*/
int PASCAL WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR szCmdLine, int sw)
{
MSG msg;

/* Call initialization procedure */
if (!AppInit(hInst,hPrev,sw,szCmdLine))
return FALSE;

/*
* Polling messages from event queue
*/
for (;;)
{
while (PeekMessage(&msg, NULL, 0, 0,PM_REMOVE))
{
if (msg.message == WM_QUIT)
return msg.wParam;

if (TranslateAccelerator(ghwndApp, ghAccel, &msg))
continue;

TranslateMessage(&msg);
DispatchMessage(&msg);
}

WaitMessage();
}

// not reached
return msg.wParam;
}


/*----------------------------------------------------------------------------*\
| AppWndProc( hwnd, uiMessage, wParam, lParam ) |
| |
| Description: |
| The window proc for the app's main (tiled) window. This processes all |
| of the parent window's messages. |
| |
| Arguments: |
| hwnd window handle for the window |
| msg message number |
| wParam message-dependent |
| lParam message-dependent |
| |
| Returns: |
| 0 if processed, nonzero if ignored |
| |
\*----------------------------------------------------------------------------*/
LONG WINAPI AppWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
PAINTSTRUCT ps;
HDC hdc;
RECT rc;
int cxBorder, cyBorder, cy;

switch (msg) {

//
//
case WM_CREATE:
break;

case WM_COMMAND:
return AppCommand(hwnd,msg,wParam,lParam);

case WM_INITMENU:
// we can start capture if not capturing already
EnableMenuItem((HMENU)wParam, MENU_START_CAP,
(!gcap.fCapturing) ? MF_ENABLED :
MF_GRAYED);
// we can stop capture if it's currently capturing
EnableMenuItem((HMENU)wParam, MENU_STOP_CAP,
(gcap.fCapturing) ? MF_ENABLED : MF_GRAYED);

// We can bring up a dialog if the graph is stopped
EnableMenuItem((HMENU)wParam, MENU_DIALOG0, !gcap.fCapturing ?
MF_ENABLED : MF_GRAYED);
EnableMenuItem((HMENU)wParam, MENU_DIALOG1, !gcap.fCapturing ?
MF_ENABLED : MF_GRAYED);
EnableMenuItem((HMENU)wParam, MENU_DIALOG2, !gcap.fCapturing ?
MF_ENABLED : MF_GRAYED);
EnableMenuItem((HMENU)wParam, MENU_DIALOG3, !gcap.fCapturing ?
MF_ENABLED : MF_GRAYED);
EnableMenuItem((HMENU)wParam, MENU_DIALOG4, !gcap.fCapturing ?
MF_ENABLED : MF_GRAYED);
EnableMenuItem((HMENU)wParam, MENU_DIALOG5, !gcap.fCapturing ?
MF_ENABLED : MF_GRAYED);
EnableMenuItem((HMENU)wParam, MENU_DIALOG6, !gcap.fCapturing ?
MF_ENABLED : MF_GRAYED);
EnableMenuItem((HMENU)wParam, MENU_DIALOG7, !gcap.fCapturing ?
MF_ENABLED : MF_GRAYED);
EnableMenuItem((HMENU)wParam, MENU_DIALOG8, !gcap.fCapturing ?
MF_ENABLED : MF_GRAYED);
EnableMenuItem((HMENU)wParam, MENU_DIALOG9, !gcap.fCapturing ?
MF_ENABLED : MF_GRAYED);

// toggles capturing audio or not - can't be capturing right now
// and we must have an audio capture device created
EnableMenuItem((HMENU)wParam, MENU_CAP_AUDIO,
(!gcap.fCapturing && gcap.pACap) ? MF_ENABLED : MF_GRAYED);
// are we capturing audio?
CheckMenuItem((HMENU)wParam, MENU_CAP_AUDIO,
(gcap.fCapAudio) ? MF_CHECKED : MF_UNCHECKED);
// change audio formats when not capturing
EnableMenuItem((HMENU)wParam, MENU_AUDIOFORMAT, (gcap.fCapAudio &&
!gcap.fCapturing) ? MF_ENABLED : MF_GRAYED);
// change frame rate when not capturing
EnableMenuItem((HMENU)wParam, MENU_FRAMERATE,
!gcap.fCapturing ? MF_ENABLED : MF_GRAYED);
// change time limit when not capturing
EnableMenuItem((HMENU)wParam, MENU_TIMELIMIT,
!gcap.fCapturing ? MF_ENABLED : MF_GRAYED);
// change capture file name when not capturing
EnableMenuItem((HMENU)wParam, MENU_SET_CAP_FILE,
!gcap.fCapturing ? MF_ENABLED : MF_GRAYED);
// pre-allocate capture file when not capturing
EnableMenuItem((HMENU)wParam, MENU_ALLOC_CAP_FILE,
!gcap.fCapturing ? MF_ENABLED : MF_GRAYED);
// can save capture file when not capturing
EnableMenuItem((HMENU)wParam, MENU_SAVE_CAP_FILE,
!gcap.fCapturing ? MF_ENABLED : MF_GRAYED);
// do we want preview?
CheckMenuItem((HMENU)wParam, MENU_PREVIEW,
(gcap.fWantPreview) ? MF_CHECKED : MF_UNCHECKED);
// can toggle preview if not capturing
EnableMenuItem((HMENU)wParam, MENU_PREVIEW,
!gcap.fCapturing ? MF_ENABLED : MF_GRAYED);

// which is the master stream?
EnableMenuItem((HMENU)wParam, MENU_NOMASTER,
!gcap.fCapturing ? MF_ENABLED : MF_GRAYED);
CheckMenuItem((HMENU)wParam, MENU_NOMASTER,
(gcap.iMasterStream == -1) ? MF_CHECKED : MF_UNCHECKED);
EnableMenuItem((HMENU)wParam, MENU_AUDIOMASTER,
!gcap.fCapturing ? MF_ENABLED : MF_GRAYED);
CheckMenuItem((HMENU)wParam, MENU_AUDIOMASTER,
(gcap.iMasterStream == 1) ? MF_CHECKED : MF_UNCHECKED);
EnableMenuItem((HMENU)wParam, MENU_VIDEOMASTER,
!gcap.fCapturing ? MF_ENABLED : MF_GRAYED);
CheckMenuItem((HMENU)wParam, MENU_VIDEOMASTER,
(gcap.iMasterStream == 0) ? MF_CHECKED : MF_UNCHECKED);

// can't select a new capture device when capturing
EnableMenuItem((HMENU)wParam, MENU_VDEVICE0,
!gcap.fCapturing ? MF_ENABLED : MF_GRAYED);
EnableMenuItem((HMENU)wParam, MENU_VDEVICE1,
!gcap.fCapturing ? MF_ENABLED : MF_GRAYED);
EnableMenuItem((HMENU)wParam, MENU_VDEVICE2,
!gcap.fCapturing ? MF_ENABLED : MF_GRAYED);
EnableMenuItem((HMENU)wParam, MENU_VDEVICE3,
!gcap.fCapturing ? MF_ENABLED : MF_GRAYED);
EnableMenuItem((HMENU)wParam, MENU_VDEVICE4,
!gcap.fCapturing ? MF_ENABLED : MF_GRAYED);
EnableMenuItem((HMENU)wParam, MENU_VDEVICE5,
!gcap.fCapturing ? MF_ENABLED : MF_GRAYED);
EnableMenuItem((HMENU)wParam, MENU_VDEVICE6,
!gcap.fCapturing ? MF_ENABLED : MF_GRAYED);
EnableMenuItem((HMENU)wParam, MENU_VDEVICE7,
!gcap.fCapturing ? MF_ENABLED : MF_GRAYED);
EnableMenuItem((HMENU)wParam, MENU_VDEVICE8,
!gcap.fCapturing ? MF_ENABLED : MF_GRAYED);
EnableMenuItem((HMENU)wParam, MENU_VDEVICE9,
!gcap.fCapturing ? MF_ENABLED : MF_GRAYED);
EnableMenuItem((HMENU)wParam, MENU_ADEVICE0,
!gcap.fCapturing ? MF_ENABLED : MF_GRAYED);
EnableMenuItem((HMENU)wParam, MENU_ADEVICE1,
!gcap.fCapturing ? MF_ENABLED : MF_GRAYED);
EnableMenuItem((HMENU)wParam, MENU_ADEVICE2,
!gcap.fCapturing ? MF_ENABLED : MF_GRAYED);
EnableMenuItem((HMENU)wParam, MENU_ADEVICE3,
!gcap.fCapturing ? MF_ENABLED : MF_GRAYED);
EnableMenuItem((HMENU)wParam, MENU_ADEVICE4,
!gcap.fCapturing ? MF_ENABLED : MF_GRAYED);
EnableMenuItem((HMENU)wParam, MENU_ADEVICE5,
!gcap.fCapturing ? MF_ENABLED : MF_GRAYED);
EnableMenuItem((HMENU)wParam, MENU_ADEVICE6,
!gcap.fCapturing ? MF_ENABLED : MF_GRAYED);
EnableMenuItem((HMENU)wParam, MENU_ADEVICE7,
!gcap.fCapturing ? MF_ENABLED : MF_GRAYED);
EnableMenuItem((HMENU)wParam, MENU_ADEVICE8,
!gcap.fCapturing ? MF_ENABLED : MF_GRAYED);
EnableMenuItem((HMENU)wParam, MENU_ADEVICE9,
!gcap.fCapturing ? MF_ENABLED : MF_GRAYED);

break;

//
// We're out of here!
//
case WM_DESTROY:
DbgTerminate();
CoUninitialize();
PostQuitMessage(0);
break;

//
//
case WM_CLOSE:
StopPreview();
StopCapture();
TearDownGraph();
FreeCapFilters();
// store current settings in win.ini for next time
WriteProfileString("annie", "CaptureFile", gcap.szCaptureFile);
char ach[10];
wsprintf(ach, "%d", gcap.iVideoDevice);
WriteProfileString("annie", "VideoDevice", ach);
wsprintf(ach, "%d", gcap.iAudioDevice);
WriteProfileString("annie", "AudioDevice", ach);
wsprintf(ach, "%d", (int)(10000000 / gcap.FrameRate));
WriteProfileString("annie", "FrameRate", ach);
wsprintf(ach, "%d", gcap.fCapAudio);
WriteProfileString("annie", "CaptureAudio", ach);
wsprintf(ach, "%d", gcap.fWantPreview);
WriteProfileString("annie", "WantPreview", ach);
wsprintf(ach, "%d", gcap.iMasterStream);
WriteProfileString("annie", "MasterStream", ach);
break;

case WM_ERASEBKGND:
break;

// ESC will stop capture
case WM_KEYDOWN:
if ((GetAsyncKeyState(VK_ESCAPE) & 0x01) &&
gcap.fCapturing)
StopCapture();
if (gcap.fWantPreview) {
BuildPreviewGraph();
StartPreview();
}
break;

case WM_PAINT:
hdc = BeginPaint(hwnd,&ps);

// nothing to do

EndPaint(hwnd,&ps);
break;

case WM_TIMER:
// update our status bar with #captured, #dropped
UpdateStatus(FALSE);

// is our time limit up?
if (gcap.fUseTimeLimit) {
if ((timeGetTime() - gcap.lCapStartTime) / 1000 >=
gcap.dwTimeLimit) {
StopCapture();
if (gcap.fWantPreview) {
BuildPreviewGraph();
StartPreview();
}
}
}
break;

case WM_SIZE:
// make the preview window fit inside our window, taking up
// all of our client area except for the status window at the
// bottom
GetClientRect(ghwndApp, &rc);
cxBorder = GetSystemMetrics(SM_CXBORDER);
cyBorder = GetSystemMetrics(SM_CYBORDER);
cy = statusGetHeight() + cyBorder;
MoveWindow(ghwndStatus, -cxBorder, rc.bottom - cy,
rc.right + (2 * cxBorder), cy + cyBorder, TRUE);
rc.bottom -= cy;
// this is the video renderer window showing the preview
if (gcap.pVW)
gcap.pVW->SetWindowPosition(0, 0, rc.right, rc.bottom);
break;

case WM_FGNOTIFY:
// uh-oh, something went wrong while capturing - the filtergraph
// will send us events like EC_COMPLETE, EC_USERABORT and the one
// we care about, EC_ERRORABORT.
if (gcap.pME && gcap.fCapturing) {
LONG event, l1, l2;
while (gcap.pME->GetEvent(&event, &l1, &l2, 0) == S_OK) {
if (event == EC_ERRORABORT) {
// pME will go away in BuildPreviewGraph
gcap.pME->FreeEventParams(event, l1, l2);
StopCapture();
if (gcap.fWantPreview) {
BuildPreviewGraph();
StartPreview();
}
ErrMsg("ERROR during capture - disk possibly full");
break;
}
gcap.pME->FreeEventParams(event, l1, l2);
}
}
break;

}
return DefWindowProc(hwnd,msg,wParam,lParam);
}


// Make a graph builder object we can use for capture graph building
//
BOOL MakeBuilder()
{
// we have one already
if (gcap.pBuilder)
return TRUE;

HRESULT hr = CoCreateInstance((REFCLSID)CLSID_CaptureGraphBuilder,
NULL, CLSCTX_INPROC, (REFIID)IID_ICaptureGraphBuilder,
(void **)&gcap.pBuilder);
return (hr == NOERROR) ? TRUE : FALSE;
}


// Make a graph object we can use for capture graph building
//
BOOL MakeGraph()
{
// we have one already
if (gcap.pFg)
return TRUE;

HRESULT hr = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC,
IID_IGraphBuilder, (LPVOID *)&gcap.pFg);
return (hr == NOERROR) ? TRUE : FALSE;
}


// make sure the preview window inside our window is as big as the
// dimensions of captured video, or some capture cards won't show a preview.
// (Also, it helps people tell what size video they're capturing)
// We will resize our app's window big enough so that once the status bar
// is positioned at the bottom there will be enough room for the preview
// window to be w x h
//
void ResizeWindow(int w, int h)
{
RECT rcW, rcC;
int xExtra, yExtra;
int cyBorder = GetSystemMetrics(SM_CYBORDER);

GetWindowRect(ghwndApp, &rcW);
GetClientRect(ghwndApp, &rcC);
xExtra = rcW.right - rcW.left - rcC.right;
yExtra = rcW.bottom - rcW.top - rcC.bottom + cyBorder + statusGetHeight();

rcC.right = w;
rcC.bottom = h;
SetWindowPos(ghwndApp, NULL, 0, 0, rcC.right + xExtra,
rcC.bottom + yExtra, SWP_NOZORDER | SWP_NOMOVE);
}


// Free all the reference counts we have so the graph can go away
//
void TearDownGraph()
{
if (gcap.pBuilder)
gcap.pBuilder->Release();
gcap.pBuilder = NULL;
if (gcap.pSink)
gcap.pSink->Release();
gcap.pSink = NULL;
if (gcap.pConfigAviMux)
gcap.pConfigAviMux->Release();
gcap.pConfigAviMux = NULL;
if (gcap.pRender)
gcap.pRender->Release();
gcap.pRender = NULL;
if (gcap.pVW)
gcap.pVW->Release();
gcap.pVW = NULL;
if (gcap.pME)
gcap.pME->Release();
gcap.pME = NULL;
if (gcap.pFg)
gcap.pFg->Release();
gcap.pFg = NULL;
gcap.fCaptureGraphBuilt = FALSE;
gcap.fPreviewGraphBuilt = FALSE;
}


// create the capture filters. We need to talk to the video capture filter
// to bring up the format, source and display dialogs
//
BOOL InitCapFilters()
{
HRESULT hr;
BOOL f;
UINT uIndex = 0;

f = MakeBuilder();
if (!f) {
ErrMsg("Cannot instantiate graph builder");
return FALSE;
}

//
// First, we need a Video Capture filter, and some interfaces
//

// we already have a video capture filter
if (gcap.pVCap)
goto MakeAudio;

// !!! There's got to be a way to cache these from building the menu
// Enumerate all the video devices. We want #gcap.iVideoDevice
ICreateDevEnum *pCreateDevEnum;
hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER,
IID_ICreateDevEnum, (void**)&pCreateDevEnum);
if (hr != NOERROR)
return FALSE;
IEnumMoniker *pEm;
hr = pCreateDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory,
&pEm, 0);
pCreateDevEnum->Release();
if (hr != NOERROR)
return FALSE;
pEm->Reset();
ULONG cFetched;
IMoniker *pM;
gcap.pVCap = NULL;
while(hr = pEm->Next(1, &pM, &cFetched), hr==S_OK)
{
// this is the one we want. Get its name, and instantiate it.
if ((int)uIndex == gcap.iVideoDevice) {
IPropertyBag *pBag;
gcap.achFriendlyName[0] = 0;
hr = pM->BindToStorage(0, 0, IID_IPropertyBag, (void **)&pBag);
if(SUCCEEDED(hr)) {
VARIANT var;
var.vt = VT_BSTR;
hr = pBag->Read(L"FriendlyName", &var, NULL);
if (hr == NOERROR) {
WideCharToMultiByte(CP_ACP, 0, var.bstrVal, -1,
gcap.achFriendlyName, 80, NULL, NULL);
SysFreeString(var.bstrVal);
}
pBag->Release();
}
hr = pM->BindToObject(0, 0, IID_IBaseFilter, (void**)&gcap.pVCap);
pM->Release();
break;
}
pM->Release();
uIndex++;
}
pEm->Release();
if (gcap.pVCap == NULL) {
ErrMsg("Error %x: Cannot create video capture filter", hr);
goto InitCapFiltersFail;
}

// we use this interface to get the name of the driver
// Don't worry if it doesn't work: This interface may not be available
// until the pin is connected, or it may not be available at all.
hr = gcap.pBuilder->FindInterface(&PIN_CATEGORY_CAPTURE, gcap.pVCap,
IID_IAMVideoCompression, (void **)&gcap.pVC);

// !!! What if this interface isn't supported?
// we use this interface to set the frame rate and get the capture size
hr = gcap.pBuilder->FindInterface(&PIN_CATEGORY_CAPTURE, gcap.pVCap,
IID_IAMStreamConfig, (void **)&gcap.pVSC);
if (hr != NOERROR) {
ErrMsg("Error %x: Cannot find VCapture:IAMStreamConfig", hr);
} else {
AM_MEDIA_TYPE *pmt;
hr = gcap.pVSC->GetFormat(&pmt); // current capture format
if (hr == NOERROR) {
// resize our window to the new capture size
ResizeWindow(HEADER(pmt->pbFormat)->biWidth,
HEADER(pmt->pbFormat)->biHeight);
DeleteMediaType(pmt);
}
}

// we use this interface to bring up the 3 dialogs
// NOTE: Only the VfW capture filter supports this. This app only brings
// up dialogs for legacy VfW capture drivers, since only those have dialogs
hr = gcap.pBuilder->FindInterface(&PIN_CATEGORY_CAPTURE, gcap.pVCap,
IID_IAMVfwCaptureDialogs, (void **)&gcap.pDlg);

MakeAudio:

// we have a filter made already
if (gcap.pACap != NULL)
return TRUE;

//
// We want an audio capture filter and some interfaces
//

// !!! There's got to be a way to cache these from building the menu
// Enumerate all the audio devices. We want #gcap.iAudioDevice
hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER,
IID_ICreateDevEnum, (void**)&pCreateDevEnum);
if (hr != NOERROR)
return FALSE;
uIndex = 0;
hr = pCreateDevEnum->CreateClassEnumerator(CLSID_AudioInputDeviceCategory,
&pEm, 0);
pCreateDevEnum->Release();
if (hr != NOERROR) {
gcap.fCapAudio = FALSE; // better not try
return FALSE;
}
pEm->Reset();
gcap.pACap = NULL;
while(hr = pEm->Next(1, &pM, &cFetched), hr==S_OK)
{
// this is the one we want!
if ((int)uIndex == gcap.iAudioDevice) {
hr = pM->BindToObject(0, 0, IID_IBaseFilter, (void**)&gcap.pACap);
pM->Release();
break;
}
pM->Release();
uIndex++;
}
pEm->Release();
if (gcap.pACap == NULL) {
gcap.fCapAudio = FALSE; // better not try
ErrMsg("Cannot create audio capture filter");
goto InitCapFiltersFail;
}

// !!! What if this interface isn't supported?
// we use this interface to set the captured wave format
hr = gcap.pBuilder->FindInterface(&PIN_CATEGORY_CAPTURE, gcap.pACap,
IID_IAMStreamConfig, (void **)&gcap.pASC);
if (hr != NOERROR) {
ErrMsg("Cannot find ACapture:IAMStreamConfig");
}

return TRUE;

InitCapFiltersFail:
FreeCapFilters();
return FALSE;
}


// all done with the capture filters and the graph builder
//
void FreeCapFilters()
{
if (gcap.pBuilder)
gcap.pBuilder->Release();
gcap.pBuilder = NULL;
if (gcap.pVCap)

gcap.pVCap->Release(); 
gcap.pVCap = NULL;
if (gcap.pACap)
gcap.pACap->Release();
gcap.pACap = NULL;
if (gcap.pASC)
gcap.pASC->Release();
gcap.pASC = NULL;
if (gcap.pVSC)
gcap.pVSC->Release();
gcap.pVSC = NULL;
if (gcap.pDF)
gcap.pDF->Release();
gcap.pDF = NULL;
if (gcap.pVC)
gcap.pVC->Release();
gcap.pVC = NULL;
if (gcap.pDlg)
gcap.pDlg->Release();
gcap.pDlg = NULL;
}


// build the capture graph!
//
BOOL BuildCaptureGraph()
{
int cy, cyBorder;
HRESULT hr;
BOOL f;
AM_MEDIA_TYPE *pmt;

// we have one already
if (gcap.fCaptureGraphBuilt)
return TRUE;

// No rebuilding while we're running
if (gcap.fCapturing || gcap.fPreviewing)
return FALSE;

// We don't have the necessary capture filters
if (gcap.pVCap == NULL)
return FALSE;
if (gcap.pACap == NULL && gcap.fCapAudio)
return FALSE;

// no capture file name yet... we need one first
if (gcap.szCaptureFile[0] == 0) {
f = SetCaptureFile(ghwndApp);
if (!f)
return f;
}

// we already have another graph built... tear down the old one
if (gcap.fPreviewGraphBuilt)
TearDownGraph();

f = MakeBuilder();
if (!f) {
ErrMsg("Cannot instantiate graph builder");
return FALSE;
}

//
// We need a rendering section that will write the capture file out in AVI
// file format
//

WCHAR wach[_MAX_PATH];
MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, gcap.szCaptureFile, -1, wach,
_MAX_PATH);
GUID guid = MEDIASUBTYPE_Avi;
hr = gcap.pBuilder->SetOutputFileName(&guid, wach, &gcap.pRender,
&gcap.pSink);
if (hr != NOERROR) {
ErrMsg("Cannot set output file");
goto SetupCaptureFail;
}

// Now tell the AVIMUX to write out AVI files that old apps can read properly.
// If we don't, most apps won't be able to tell where the keyframes are,
// slowing down editing considerably
// Doing this will cause one seek (over the area the index will go) when
// you capture past 1 Gig, but that's no big deal.
// NOTE: This is on by default, so it's not necessary to turn it on

// Also, set the proper MASTER STREAM

hr = gcap.pRender->QueryInterface(IID_IConfigAviMux,
(void **)&gcap.pConfigAviMux);
if (hr == NOERROR && gcap.pConfigAviMux) {
gcap.pConfigAviMux->SetOutputCompatibilityIndex(TRUE);
if (gcap.fCapAudio) {
hr = gcap.pConfigAviMux->SetMasterStream(gcap.iMasterStream);
if (hr != NOERROR)
ErrMsg("SetMasterStream failed!");
}
}

//
// The graph builder created a filtergraph to do that. Find out what it is,
// and put the video capture filter in the graph too.
//

hr = gcap.pBuilder->GetFiltergraph(&gcap.pFg);
if (hr != NOERROR) {
ErrMsg("Cannot get filtergraph");
goto SetupCaptureFail;
}

hr = gcap.pFg->AddFilter(gcap.pVCap, NULL);
if (hr != NOERROR) {
ErrMsg("Cannot add vidcap to filtergraph");
goto SetupCaptureFail;
}

//
// Now, add the audio capture filter to the graph?
//

if (gcap.fCapAudio) {
hr = gcap.pFg->AddFilter(gcap.pACap, NULL);
if (hr != NOERROR) {
ErrMsg("Cannot add audcap to filtergraph");
goto SetupCaptureFail;
}
}

//
// Render the video capture and preview pins - we may not have preview, so
// don't worry if it doesn't work
//

hr = gcap.pBuilder->RenderStream(&PIN_CATEGORY_CAPTURE, gcap.pVCap,
NULL, gcap.pRender);
if (hr != NOERROR) {
ErrMsg("Cannot render video capture stream");
goto SetupCaptureFail;
}

hr = gcap.pBuilder->RenderStream(&PIN_CATEGORY_PREVIEW, gcap.pVCap,
NULL, NULL);

//
// Render the audio capture pin?
//

if (gcap.fCapAudio) {
hr = gcap.pBuilder->RenderStream(&PIN_CATEGORY_CAPTURE,
gcap.pACap, NULL, gcap.pRender);
if (hr != NOERROR) {
ErrMsg("Cannot render audio capture stream");
goto SetupCaptureFail;
}
}

//
// Get the preview window to be a child of our app's window
//

// this will go through a possible decoder, find the video renderer it's
// connected to, and get the IVideoWindow interface on it
hr = gcap.pBuilder->FindInterface(&PIN_CATEGORY_PREVIEW, gcap.pVCap,
IID_IVideoWindow, (void **)&gcap.pVW);
if (hr != NOERROR) {
ErrMsg("This graph cannot preview");
} else {
RECT rc;
gcap.pVW->put_Owner((long)ghwndApp); // We own the window now
gcap.pVW->put_WindowStyle(WS_CHILD); // you are now a child
// give the preview window all our space but where the status bar is
GetClientRect(ghwndApp, &rc);
cyBorder = GetSystemMetrics(SM_CYBORDER);
cy = statusGetHeight() + cyBorder;
rc.bottom -= cy;
gcap.pVW->SetWindowPosition(0, 0, rc.right, rc.bottom); // be this big
gcap.pVW->put_Visible(OATRUE);
}

// now tell it what frame rate to capture at. Just find the format it
// is capturing with, and leave everything alone but change the frame rate
hr = E_FAIL;
if (gcap.pVSC) {
hr = gcap.pVSC->GetFormat(&pmt);
if (hr == NOERROR) {
VIDEOINFOHEADER *pvi = (VIDEOINFOHEADER *)pmt->pbFormat;
pvi->AvgTimePerFrame = (LONGLONG)(10000000 / gcap.FrameRate);
hr = gcap.pVSC->SetFormat(pmt);
DeleteMediaType(pmt);
}
}
if (hr != NOERROR)
ErrMsg("Cannot set frame rate for capture");

// now ask the filtergraph to tell us when something is completed or aborted
// (EC_COMPLETE, EC_USERABORT, EC_ERRORABORT). This is how we will find out
// if the disk gets full while capturing
hr = gcap.pFg->QueryInterface(IID_IMediaEventEx, (void **)&gcap.pME);
if (hr == NOERROR) {
gcap.pME->SetNotifyWindow((LONG)ghwndApp, WM_FGNOTIFY, 0);
}

// All done.

// potential debug output - what the graph looks like
// DumpGraph(gcap.pFg, 1);

gcap.fCaptureGraphBuilt = TRUE;
return TRUE;

SetupCaptureFail:
TearDownGraph();
return FALSE;
}



// build the preview graph!
//
// !!! PLEASE NOTE !!! Some new WDM devices have totally separate capture
// and preview settings. An application that wishes to preview and then
// capture may have to set the preview pin format using IAMStreamConfig on the
// preview pin, and then again on the capture pin to capture with that format.
// In this sample app, there is a separate page to set the settings on the
// capture pin and one for the preview pin. To avoid the user
// having to enter the same settings in 2 dialog boxes, an app can have its own
// UI for choosing a format (the possible formats can be enumerated using
// IAMStreamConfig) and then the app can programmatically call IAMStreamConfig
// to set the format on both pins.
//
BOOL BuildPreviewGraph()
{
int cy, cyBorder;
HRESULT hr;
BOOL f;
AM_MEDIA_TYPE *pmt;
BOOL fPreviewUsingCapturePin = FALSE;

// we have one already
if (gcap.fPreviewGraphBuilt)
return TRUE;

// No rebuilding while we're running
if (gcap.fCapturing || gcap.fPreviewing)
return FALSE;

// We don't have the necessary capture filters
if (gcap.pVCap == NULL)
return FALSE;
if (gcap.pACap == NULL && gcap.fCapAudio)
return FALSE;

// we already have another graph built... tear down the old one
if (gcap.fCaptureGraphBuilt)
TearDownGraph();

f = MakeBuilder();
if (!f) {
ErrMsg("Cannot instantiate graph builder");
return FALSE;
}

//
// make a filtergraph, give it to the graph builder and put the capture filter
// in the graph
//

f = MakeGraph();
if (!f) {
ErrMsg("Cannot instantiate filtergraph");
goto SetupPreviewFail;
}
hr = gcap.pBuilder->SetFiltergraph(gcap.pFg);
if (hr != NOERROR) {
ErrMsg("Cannot give graph to builder");
goto SetupPreviewFail;
}


hr = gcap.pFg->AddFilter(gcap.pVCap, NULL);
if (hr != NOERROR) {
ErrMsg("Error %x: Cannot add vidcap to filtergraph", hr);
goto SetupPreviewFail;
}

//
// Render the preview pin - it may not work if there is no preview pin
//
// !!! So use the capture pin - what about latency/buffer issues?

hr = gcap.pBuilder->RenderStream(&PIN_CATEGORY_PREVIEW, gcap.pVCap,
NULL, NULL);
if (hr != S_OK) {
gcap.pBuilder->RenderStream(&PIN_CATEGORY_CAPTURE, gcap.pVCap,
NULL, NULL);
fPreviewUsingCapturePin = TRUE;

// !!! Since we are using the capture pin to preview, we should probably
// make the graph have no clock, so that the time stamps on the captured
// frames don't make the renderer drop any frames.

}

//
// Get the preview window to be a child of our app's window
//

// this will go through a possible decoder, find the video renderer it's
// connected to, and get the IVideoWindow interface on it
hr = gcap.pBuilder->FindInterface(
fPreviewUsingCapturePin ? &PIN_CATEGORY_CAPTURE :
&PIN_CATEGORY_PREVIEW,
gcap.pVCap, IID_IVideoWindow, (void **)&gcap.pVW);
if (hr != NOERROR) {
ErrMsg("This graph cannot preview properly");
} else {
RECT rc;
gcap.pVW->put_Owner((long)ghwndApp); // We own the window now
gcap.pVW->put_WindowStyle(WS_CHILD); // you are now a child
// give the preview window all our space but where the status bar is
GetClientRect(ghwndApp, &rc);
cyBorder = GetSystemMetrics(SM_CYBORDER);
cy = statusGetHeight() + cyBorder;
rc.bottom -= cy;
gcap.pVW->SetWindowPosition(0, 0, rc.right, rc.bottom); // be this big
gcap.pVW->put_Visible(OATRUE);
}

// now tell it what frame rate to capture at. Just find the format it
// is capturing with, and leave everything alone but change the frame rate
// No big deal if it fails. It's just for preview
if (gcap.pVSC) {
hr = gcap.pVSC->GetFormat(&pmt);
if (hr == NOERROR) {
VIDEOINFOHEADER *pvi = (VIDEOINFOHEADER *)pmt->pbFormat;
pvi->AvgTimePerFrame = (LONGLONG)(10000000 / gcap.FrameRate);
gcap.pVSC->SetFormat(pmt);
DeleteMediaType(pmt);
}
}

// All done.

// potential debug output - what the graph looks like
// DumpGraph(gcap.pFg, 1);

gcap.fPreviewGraphBuilt = TRUE;
return TRUE;

SetupPreviewFail:
TearDownGraph();
return FALSE;
}


// Start previewing
//
BOOL StartPreview()
{
BOOL f = TRUE;

// way ahead of you
if (gcap.fPreviewing)
return TRUE;

if (!gcap.fPreviewGraphBuilt)
return FALSE;

// run the graph
IMediaControl *pMC = NULL;
HRESULT hr = gcap.pFg->QueryInterface(IID_IMediaControl, (void **)&pMC);
if (SUCCEEDED(hr)) {
hr = pMC->Run();
if (FAILED(hr)) {
// stop parts that ran
pMC->Stop();
}
pMC->Release();
}
if (FAILED(hr)) {
ErrMsg("Error %x: Cannot run preview graph", hr);
return FALSE;
}

gcap.fPreviewing = TRUE;
return TRUE;
}


// stop the preview graph
//
BOOL StopPreview()
{
// way ahead of you
if (!gcap.fPreviewing) {
return FALSE;
}

// stop the graph
IMediaControl *pMC = NULL;
HRESULT hr = gcap.pFg->QueryInterface(IID_IMediaControl, (void **)&pMC);
if (SUCCEEDED(hr)) {
hr = pMC->Stop();
pMC->Release();
}
if (FAILED(hr)) {
ErrMsg("Error %x: Cannot stop preview graph", hr);
return FALSE;
}

gcap.fPreviewing = FALSE;

// !!! get rid of menu garbage
InvalidateRect(ghwndApp, NULL, TRUE);

return TRUE;
}


// start the capture graph
//
BOOL StartCapture()
{
BOOL f, fHasStreamControl;
HRESULT hr;

// way ahead of you
if (gcap.fCapturing)
return TRUE;

// or we'll get confused
if (gcap.fPreviewing)
StopPreview();

// or we'll crash
if (!gcap.fCaptureGraphBuilt)
return FALSE;

REFERENCE_TIME start = MAX_TIME, stop = MAX_TIME;
// show us a preview first? but don't capture quite yet...
hr = gcap.pBuilder->ControlStream(&PIN_CATEGORY_PREVIEW, NULL,
gcap.fWantPreview ? NULL : &start,
gcap.fWantPreview ? &stop : NULL, 0, 0);
if (SUCCEEDED(hr))
hr = gcap.pBuilder->ControlStream(&PIN_CATEGORY_CAPTURE, NULL, &start,
NULL, 0, 0);

// Do we have the ability to control capture and preview separately?
fHasStreamControl = SUCCEEDED(hr);

// prepare to run the graph
IMediaControl *pMC = NULL;
hr = gcap.pFg->QueryInterface(IID_IMediaControl, (void **)&pMC);
if (FAILED(hr)) {
ErrMsg("Error %x: Cannot get IMediaControl", hr);
return FALSE;
}

// If we were able to turn preview on yet keep capture off, then we can
// run the graph now for frame accurate start later yet still showing a
// preview. Otherwise, we can't run the graph yet without capture
// starting too, so we'll pause it so the latency between when they
// press a key and when capture begins is still small (but they won't have
// a preview while they wait to press a key)

if (fHasStreamControl)
hr = pMC->Run();
else
hr = pMC->Pause();
if (FAILED(hr)) {
// stop parts that started
pMC->Stop();
pMC->Release();
ErrMsg("Error %x: Cannot start graph", hr);
return FALSE;
}

// press a key to start capture
f = DoDialog(ghwndApp, IDD_PressAKeyDialog, (DLGPROC)PressAKeyProc, 0);
if (!f) {
pMC->Stop();
pMC->Release();
if (gcap.fWantPreview) {
BuildPreviewGraph();
StartPreview();
}
return f;
}

// Start capture NOW!
if (fHasStreamControl) {
gcap.pBuilder->ControlStream(&PIN_CATEGORY_CAPTURE, NULL, NULL, &stop,
0, 0);
} else {
hr = pMC->Run();
if (FAILED(hr)) {
// stop parts that started
pMC->Stop();
pMC->Release();
ErrMsg("Error %x: Cannot run graph", hr);
return FALSE;
}
}

pMC->Release();

// when did we start capture?
gcap.lCapStartTime = timeGetTime();

// 30 times a second I want to update my status bar - #captured, #dropped
SetTimer(ghwndApp, 1, 33, NULL);

gcap.fCapturing = TRUE;
return TRUE;
}


// stop the capture graph
//
BOOL StopCapture()
{
// way ahead of you
if (!gcap.fCapturing) {
return FALSE;
}

// stop the graph
IMediaControl *pMC = NULL;
HRESULT hr = gcap.pFg->QueryInterface(IID_IMediaControl, (void **)&pMC);
if (SUCCEEDED(hr)) {
hr = pMC->Stop();
pMC->Release();
}
if (FAILED(hr)) {
ErrMsg("Error %x: Cannot stop graph", hr);
return FALSE;
}

// when the graph was stopped
gcap.lCapStopTime = timeGetTime();

// no more status bar updates
KillTimer(ghwndApp, 1);

// one last time for the final count and all the stats
UpdateStatus(TRUE);

gcap.fCapturing = FALSE;

// !!! get rid of menu garbage
InvalidateRect(ghwndApp, NULL, TRUE);

return TRUE;
}


// Let's talk about UI for a minute. There are many programmatic interfaces
// you can use to program a capture filter or related filter to capture the
// way you want it to.... eg: IAMStreamConfig, IAMVideoCompression,
// IAMCrossbar, IAMTVTuner, IAMTVAudio, IAMAnalogVideoDecoder, IAMCameraControl,
// IAMVideoProcAmp, etc.
//
// But you probably want some UI to let the user play with all these settings.
// For new WDM-style capture devices, we offer some default UI you can use.
// The code below shows how to bring up all of the dialog boxes supported
// by any capture filters.
//
// The following code shows you how you can bring up all of the
// dialogs supported by a particular object at once on a big page with lots
// of thumb tabs. You do this by starting with an interface on the object that
// you want, and using ISpecifyPropertyPages to get the whole list, and
// OleCreatePropertyFrame to bring them all up. This way you will get custom
// property pages a filter has, too, that are not one of the standard pages that
// you know about. There are at least 9 objects that may have property pages.
// Your app already has 2 of the object pointers, the video capture filter and
// the audio capture filter (let's call them pVCap and pACap)
// 1. The video capture filter - pVCap
// 2. The video capture filter's capture pin - get this by calling
// FindInterface(&PIN_CATEGORY_CAPTURE, pVCap, IID_IPin, &pX);
// 3. The video capture filter's preview pin - get this by calling
// FindInterface(&PIN_CATEGORY_PREVIEW, pVCap, IID_IPin, &pX);
// 4. The audio capture filter - pACap
// 5. The audio capture filter's capture pin - get this by calling
// FindInterface(&PIN_CATEGORY_CAPTURE, pACap, IID_IPin, &pX);
// 6. The crossbar connected to the video capture filter - get this by calling
// FindInterface(NULL, pVCap, IID_IAMCrossbar, &pX);
// 7. The crossbar connected to the audio capture filter - get this by calling
// FindInterface(NULL, pACap, IID_IAMCrossbar, &pX);
// NOTE: may be the same as object #6. Compare IUnknowns to find out.
// 8. The TV Tuner connected to the video capture filter - get this by calling
// FindInterface(NULL, pVCap, IID_IAMTVTuner, &pX);
// 9. The TV Audio connected to the audio capture filter - get this by calling
// FindInterface(NULL, pACap, IID_IAMTVAudio, &pX);
//
// Your last choice for UI is to make your own pages, and use the results of
// your custom page to call the interfaces programmatically.


void MakeMenuOptions()
{
HRESULT hr;
HMENU hMenuSub = GetSubMenu(GetMenu(ghwndApp), 2); // Options menu

// remove any old choices from the last device
RemoveMenu(hMenuSub, 4, MF_BYPOSITION);
RemoveMenu(hMenuSub, 4, MF_BYPOSITION);
RemoveMenu(hMenuSub, 4, MF_BYPOSITION);
RemoveMenu(hMenuSub, 4, MF_BYPOSITION);
RemoveMenu(hMenuSub, 4, MF_BYPOSITION);
RemoveMenu(hMenuSub, 4, MF_BYPOSITION);
RemoveMenu(hMenuSub, 4, MF_BYPOSITION);
RemoveMenu(hMenuSub, 4, MF_BYPOSITION);
RemoveMenu(hMenuSub, 4, MF_BYPOSITION);
RemoveMenu(hMenuSub, 4, MF_BYPOSITION);

int zz = 0;
gcap.iFormatDialogPos = -1;
gcap.iSourceDialogPos = -1;
gcap.iDisplayDialogPos = -1;
gcap.iVCapDialogPos = -1;
gcap.iVCrossbarDialogPos = -1;
gcap.iTVTunerDialogPos = -1;
gcap.iACapDialogPos = -1;
gcap.iACrossbarDialogPos = -1;
gcap.iTVAudioDialogPos = -1;
gcap.iVCapCapturePinDialogPos = -1;
gcap.iVCapPreviewPinDialogPos = -1;
gcap.iACapCapturePinDialogPos = -1;

// If this device supports the old legacy UI dialogs, offer them

if (gcap.pDlg && !gcap.pDlg->HasDialog(VfwCaptureDialog_Format)) {
AppendMenuA(hMenuSub, MF_STRING, MENU_DIALOG0 + zz, "Video Format...");
gcap.iFormatDialogPos = zz++;
}
if (gcap.pDlg && !gcap.pDlg->HasDialog(VfwCaptureDialog_Source)) {
AppendMenuA(hMenuSub, MF_STRING, MENU_DIALOG0 + zz, "Video Source...");
gcap.iSourceDialogPos = zz++;
}
if (gcap.pDlg && !gcap.pDlg->HasDialog(VfwCaptureDialog_Display)) {
AppendMenuA(hMenuSub, MF_STRING, MENU_DIALOG0 + zz, "Video Display...");
gcap.iDisplayDialogPos = zz++;
}

// don't bother looking for new property pages if the old ones are supported
// or if we don't have a capture filter
if (gcap.pVCap == NULL || gcap.iFormatDialogPos != -1)
return;

// New WDM devices support new UI and new interfaces.
// Your app can use some default property
// pages for UI if you'd like (like we do here) or if you don't like our
// dialog boxes, feel free to make your own and programmatically set
// the capture options through interfaces like IAMCrossbar, IAMCameraControl
// etc.

// There are 9 objects that might support property pages. Let's go through
// them.

ISpecifyPropertyPages *pSpec;
CAUUID cauuid;

// 1. the video capture filter itself

hr = gcap.pVCap->QueryInterface(IID_ISpecifyPropertyPages, (void **)&pSpec);
if (hr == S_OK) {
hr = pSpec->GetPages(&cauuid);
if (hr == S_OK && cauuid.cElems > 0) {
AppendMenuA(hMenuSub,MF_STRING,MENU_DIALOG0+zz,"Video Capture Filter...");
gcap.iVCapDialogPos = zz++;
CoTaskMemFree(cauuid.pElems);
}
pSpec->Release();
}

// 2. The video capture capture pin

IAMStreamConfig *pSC;
hr = gcap.pBuilder->FindInterface(&PIN_CATEGORY_CAPTURE, gcap.pVCap,
IID_IAMStreamConfig, (void **)&pSC);
if (hr == S_OK) {
hr = pSC->QueryInterface(IID_ISpecifyPropertyPages, (void **)&pSpec);
if (hr == S_OK) {
hr = pSpec->GetPages(&cauuid);
if (hr == S_OK && cauuid.cElems > 0) {
AppendMenuA(hMenuSub,MF_STRING,MENU_DIALOG0+zz,"Video Capture Pin...");
gcap.iVCapCapturePinDialogPos = zz++;
CoTaskMemFree(cauuid.pElems);
}
pSpec->Release();
}
pSC->Release();
}

// 3. The video capture preview pin.
// This basically sets the format being previewed. Typically, you
// want to capture and preview using the SAME format, instead of having to
// enter the same value in 2 dialog boxes. For a discussion on this, see
// the comment above the MakePreviewGraph function.

hr = gcap.pBuilder->FindInterface(&PIN_CATEGORY_PREVIEW, gcap.pVCap,
IID_IAMStreamConfig, (void **)&pSC);
if (hr == S_OK) {
hr = pSC->QueryInterface(IID_ISpecifyPropertyPages, (void **)&pSpec);
if (hr == S_OK) {
hr = pSpec->GetPages(&cauuid);
if (hr == S_OK && cauuid.cElems > 0) {
AppendMenuA(hMenuSub,MF_STRING,MENU_DIALOG0+zz,"Video Preview Pin...");
gcap.iVCapPreviewPinDialogPos = zz++;
CoTaskMemFree(cauuid.pElems);
}
pSpec->Release();
}
pSC->Release();
}

// 4. The video crossbar

IAMCrossbar *pX;
hr = gcap.pBuilder->FindInterface(&PIN_CATEGORY_CAPTURE, gcap.pVCap,
IID_IAMCrossbar, (void **)&pX);
if (hr == S_OK) {
hr = pX->QueryInterface(IID_ISpecifyPropertyPages, (void **)&pSpec);
if (hr == S_OK) {
hr = pSpec->GetPages(&cauuid);
if (hr == S_OK && cauuid.cElems > 0) {
AppendMenuA(hMenuSub,MF_STRING,MENU_DIALOG0+zz,"Video Crossbar...");
gcap.iVCrossbarDialogPos = zz++;
CoTaskMemFree(cauuid.pElems);
}
pSpec->Release();
}
pX->Release();
}

// 5. The TVTuner

IAMTVTuner *pTV;
hr = gcap.pBuilder->FindInterface(&PIN_CATEGORY_CAPTURE, gcap.pVCap,
IID_IAMTVTuner, (void **)&pTV);
if (hr == S_OK) {
hr = pTV->QueryInterface(IID_ISpecifyPropertyPages, (void **)&pSpec);
if (hr == S_OK) {
hr = pSpec->GetPages(&cauuid);
if (hr == S_OK && cauuid.cElems > 0) {
AppendMenuA(hMenuSub,MF_STRING,MENU_DIALOG0+zz,"TV Tuner...");
gcap.iTVTunerDialogPos = zz++;
CoTaskMemFree(cauuid.pElems);
}
pSpec->Release();
}
pTV->Release();
}

// no audio capture, we're done
if (gcap.pACap == NULL)
return;

// 6. The Audio capture filter itself

hr = gcap.pACap->QueryInterface(IID_ISpecifyPropertyPages, (void **)&pSpec);
if (hr == S_OK) {
hr = pSpec->GetPages(&cauuid);
if (hr == S_OK && cauuid.cElems > 0) {
AppendMenuA(hMenuSub,MF_STRING,MENU_DIALOG0+zz,"Audio Capture Filter...");
gcap.iACapDialogPos = zz++;
CoTaskMemFree(cauuid.pElems);
}
pSpec->Release();
}

// 7. The Audio capture pin

hr = gcap.pBuilder->FindInterface(&PIN_CATEGORY_CAPTURE, gcap.pACap,
IID_IAMStreamConfig, (void **)&pSC);
if (hr == S_OK) {
hr = pSC->QueryInterface(IID_ISpecifyPropertyPages, (void **)&pSpec);
if (hr == S_OK) {
hr = pSpec->GetPages(&cauuid);
if (hr == S_OK && cauuid.cElems > 0) {
AppendMenuA(hMenuSub,MF_STRING,MENU_DIALOG0+zz,"Audio Capture Pin...");
gcap.iACapCapturePinDialogPos = zz++;
CoTaskMemFree(cauuid.pElems);
}
pSpec->Release();
}
pSC->Release();
}

// 8. The Audio Crossbar, but only if it's different than the video
// crossbar

IAMCrossbar *pXV;
hr = gcap.pBuilder->FindInterface(&PIN_CATEGORY_CAPTURE, gcap.pACap,
IID_IAMCrossbar, (void **)&pX);
if (hr == S_OK) {
BOOL fUse = TRUE;
IUnknown *pUnk1, *pUnk2;
hr = gcap.pBuilder->FindInterface(&PIN_CATEGORY_CAPTURE, gcap.pVCap,
IID_IAMCrossbar, (void **)&pXV);
if (hr == S_OK) {
if (pX->QueryInterface(IID_IUnknown, (void **)&pUnk1) == S_OK) {
if (pXV->QueryInterface(IID_IUnknown, (void **)&pUnk2)
== S_OK) {
if (pUnk1 == pUnk2)
fUse = FALSE;
pUnk2->Release();
}
pUnk1->Release();
}
pXV->Release();
}
hr = E_FAIL;
if (fUse)
hr =pX->QueryInterface(IID_ISpecifyPropertyPages, (void **)&pSpec);
if (hr == S_OK) {
hr = pSpec->GetPages(&cauuid);
if (hr == S_OK && cauuid.cElems > 0) {
AppendMenuA(hMenuSub,MF_STRING,MENU_DIALOG0+zz,"Audio Crossbar...");
gcap.iACrossbarDialogPos = zz++;
CoTaskMemFree(cauuid.pElems);
}
pSpec->Release();
}
pX->Release();
}

// 9. The TV Audio filter

IAMTVAudio *pTVA;
hr = gcap.pBuilder->FindInterface(&PIN_CATEGORY_CAPTURE, gcap.pACap,
IID_IAMTVAudio, (void **)&pTVA);
if (hr == S_OK) {
hr = pTVA->QueryInterface(IID_ISpecifyPropertyPages, (void **)&pSpec);
if (hr == S_OK) {
hr = pSpec->GetPages(&cauuid);
if (hr == S_OK && cauuid.cElems > 0) {
AppendMenuA(hMenuSub,MF_STRING,MENU_DIALOG0+zz,"TV Audio...");
gcap.iTVAudioDialogPos = zz++;
CoTaskMemFree(cauuid.pElems);
}
pSpec->Release();
}
pTVA->Release();
}
}


// how many captured/dropped so far
//
void UpdateStatus(BOOL fAllStats)
{
HRESULT hr;
LONG lDropped, lNot, lAvgFrameSize;
char ach[160];

// we use this interface to get the number of captured and dropped frames
// NOTE: We cannot query for this interface earlier, as it may not be
// available until the pin is connected
if (!gcap.pDF) {
hr = gcap.pBuilder->FindInterface(&PIN_CATEGORY_CAPTURE, gcap.pVCap,
IID_IAMDroppedFrames, (void **)&gcap.pDF);
}

// this filter can't tell us dropped frame info.
if (!gcap.pDF) {
statusUpdateStatus(ghwndStatus,
"Filter cannot report capture information");
return;
}

hr = gcap.pDF->GetNumDropped(&lDropped);
if (hr == S_OK)
hr = gcap.pDF->GetNumNotDropped(&lNot);
if (hr != S_OK)
return;

if (!fAllStats) {
LONG lTime = timeGetTime() - gcap.lCapStartTime;
wsprintf(ach, "Captured %d frames (%d dropped) %d.%dsec", lNot,
lDropped, lTime / 1000,
lTime / 100 - lTime / 1000 * 10);
statusUpdateStatus(ghwndStatus, ach);
return;
}

// we want all possible stats, including capture time and actual acheived
// frame rate and data rate (as opposed to what we tried to get). These
// numbers are an indication that though we dropped frames just now, if we
// chose a data rate and frame rate equal to the numbers I'm about to
// print, we probably wouldn't drop any frames.

// average size of frame captured
hr = gcap.pDF->GetAverageFrameSize(&lAvgFrameSize);
if (hr != S_OK)
return;

// how long capture lasted
LONG lDurMS = gcap.lCapStopTime - gcap.lCapStartTime;
double flFrame; // acheived frame rate
LONG lData; // acheived data rate

if (lDurMS > 0) {
flFrame = (double)(LONGLONG)lNot * 1000. /
(double)(LONGLONG)lDurMS;
lData = (LONG)(LONGLONG)(lNot / (double)(LONGLONG)lDurMS *
1000. * (double)(LONGLONG)lAvgFrameSize);
} else {
flFrame = 0.;
lData = 0;
}

wsprintf(ach, "Captured %d frames in %d.%d sec (%d dropped): %d.%d fps %d.%d Meg/sec",
lNot, lDurMS / 1000, lDurMS / 100 - lDurMS / 1000 * 10,
lDropped, (int)flFrame,
(int)(flFrame * 10.) - (int)flFrame * 10,
lData / 1000000,
lData / 1000 - (lData / 1000000 * 1000));
statusUpdateStatus(ghwndStatus, ach);
}


// Check the devices we're currently using and make filters for them
//
void ChooseDevices(int idV, int idA)
{
#define VERSIZE 40
#define DESCSIZE 80
int versize = VERSIZE;
int descsize = DESCSIZE;
WCHAR wachVer[VERSIZE], wachDesc[DESCSIZE];
char achStatus[VERSIZE + DESCSIZE + 5], achDesc[DESCSIZE], achVer[VERSIZE];

if (idV != gcap.iVideoDevice) {
// uncheck the old, check the new
if (gcap.iVideoDevice >= 0) // might be uninitialized
CheckMenuItem(GetMenu(ghwndApp), MENU_VDEVICE0 + gcap.iVideoDevice,

MF_UNCHECKED);  
CheckMenuItem(GetMenu(ghwndApp), MENU_VDEVICE0 + idV, MF_CHECKED);
}

if (idA != gcap.iAudioDevice) {
// uncheck the old, check the new
if (gcap.iAudioDevice >= 0) // might be uninitialized
CheckMenuItem(GetMenu(ghwndApp), MENU_ADEVICE0 + gcap.iAudioDevice,
MF_UNCHECKED);
CheckMenuItem(GetMenu(ghwndApp), MENU_ADEVICE0 + idA, MF_CHECKED);
}

// they chose a new device. rebuild the graphs
if (gcap.iVideoDevice != idV || gcap.iAudioDevice != idA) {
gcap.iVideoDevice = idV;
gcap.iAudioDevice = idA;
if (gcap.fPreviewing)
StopPreview();
if (gcap.fCaptureGraphBuilt || gcap.fPreviewGraphBuilt)
TearDownGraph();
FreeCapFilters();
InitCapFilters();
if (gcap.fWantPreview) { // were we previewing?
BuildPreviewGraph();
StartPreview();
}
MakeMenuOptions();// the UI choices change per device
}

gcap.iVideoDevice = idV;
gcap.iAudioDevice = idA;

// Put the video driver name in the status bar - if the filter supports
// IAMVideoCompression::GetInfo, that's the best way to get the name and
// the version. Otherwise use the name we got from device enumeration
// as a fallback.
if (gcap.pVC) {
HRESULT hr = gcap.pVC->GetInfo(wachVer, &versize, wachDesc, &descsize,
NULL, NULL, NULL, NULL);
if (hr == S_OK) {
WideCharToMultiByte(CP_ACP, 0, wachVer, -1, achVer, VERSIZE, NULL,
NULL);
WideCharToMultiByte(CP_ACP, 0, wachDesc, -1, achDesc, DESCSIZE,
NULL, NULL);
wsprintf(achStatus, "%s - %s", achDesc, achVer);
statusUpdateStatus(ghwndStatus, achStatus);
return;
}
}
statusUpdateStatus(ghwndStatus, gcap.achFriendlyName);
}


// put all installed video and audio devices in the menus
//
void AddDevicesToMenu()
{
UINT uIndex = 0;
HMENU hMenuSub;
HRESULT hr;

hMenuSub = GetSubMenu(GetMenu(ghwndApp), 1); // Devices menu

// remove the bogus separator
RemoveMenu(hMenuSub, 0, 0);

// enumerate all video capture devices
ICreateDevEnum *pCreateDevEnum;
hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER,
IID_ICreateDevEnum, (void**)&pCreateDevEnum);
if (hr != NOERROR)
goto EnumAudio;
IEnumMoniker *pEm;
hr = pCreateDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory,
&pEm, 0);
pCreateDevEnum->Release();
if (hr != NOERROR) {
ErrMsg("Sorry, you have no video capture hardware");
goto EnumAudio;
}
pEm->Reset();
ULONG cFetched;
IMoniker *pM;
while(hr = pEm->Next(1, &pM, &cFetched), hr==S_OK)
{
IPropertyBag *pBag;
hr = pM->BindToStorage(0, 0, IID_IPropertyBag, (void **)&pBag);
if(SUCCEEDED(hr)) {
VARIANT var;
var.vt = VT_BSTR;
hr = pBag->Read(L"FriendlyName", &var, NULL);
if (hr == NOERROR) {
char achName[80];
WideCharToMultiByte(CP_ACP, 0, var.bstrVal, -1, achName, 80,
NULL, NULL);
AppendMenuA(hMenuSub, MF_STRING, MENU_VDEVICE0 + uIndex,
achName);
SysFreeString(var.bstrVal);
}
pBag->Release();
}
pM->Release();
uIndex++;
}
pEm->Release();

// separate the video and audio devices
AppendMenuA(hMenuSub, MF_SEPARATOR, 0, NULL);

EnumAudio:

// enumerate all audio capture devices
uIndex = 0;
hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER,
IID_ICreateDevEnum, (void**)&pCreateDevEnum);
if (hr != NOERROR)
return;
hr = pCreateDevEnum->CreateClassEnumerator(CLSID_AudioInputDeviceCategory,
&pEm, 0);
pCreateDevEnum->Release();
if (hr != NOERROR)
return;
pEm->Reset();
while(hr = pEm->Next(1, &pM, &cFetched), hr==S_OK)
{
IPropertyBag *pBag;
hr = pM->BindToStorage(0, 0, IID_IPropertyBag, (void **)&pBag);
if(SUCCEEDED(hr)) {
VARIANT var;
var.vt = VT_BSTR;
hr = pBag->Read(L"FriendlyName", &var, NULL);
if (hr == NOERROR) {
char achName[80];
WideCharToMultiByte(CP_ACP, 0, var.bstrVal, -1, achName, 80,
NULL, NULL);
AppendMenuA(hMenuSub, MF_STRING, MENU_ADEVICE0 + uIndex,
achName);
SysFreeString(var.bstrVal);
}
pBag->Release();
}
pM->Release();
uIndex++;
}
pEm->Release();
}


// let them pick a frame rate
//
void ChooseFrameRate()
{
HRESULT hr;
double rate = gcap.FrameRate;

DoDialog(ghwndApp, IDD_FrameRateDialog, (DLGPROC)FrameRateProc, 0);

// new frame rate?
if (gcap.FrameRate != rate) {
if (gcap.fPreviewing)
StopPreview();
// now tell it what frame rate to capture at. Just find the format it
// is capturing with, and leave everything else alone
if (gcap.pVSC) {
AM_MEDIA_TYPE *pmt;
hr = gcap.pVSC->GetFormat(&pmt);
if (hr == NOERROR) {
VIDEOINFOHEADER *pvi = (VIDEOINFOHEADER *)pmt->pbFormat;
pvi->AvgTimePerFrame = (LONGLONG)(10000000 / gcap.FrameRate);
gcap.pVSC->SetFormat(pmt);
DeleteMediaType(pmt);
}
}
if (gcap.fWantPreview) // we were previewing
StartPreview();
}
}


// let them set a capture time limit
//
void ChooseTimeLimit()
{
DoDialog(ghwndApp, IDD_TimeLimitDialog, (DLGPROC)TimeLimitProc, 0);
}


// choose an audio capture format using ACM
//
void ChooseAudioFormat()
{
ACMFORMATCHOOSE cfmt;
DWORD dwSize;
LPWAVEFORMATEX lpwfx;
AM_MEDIA_TYPE *pmt;

// there's no point if we can't set a new format
if (gcap.pASC == NULL)
return;

// What's the largest format size around?
acmMetrics(NULL, ACM_METRIC_MAX_SIZE_FORMAT, &dwSize);
HRESULT hr = gcap.pASC->GetFormat(&pmt);
if (hr != NOERROR)
return;
lpwfx = (LPWAVEFORMATEX)pmt->pbFormat;
dwSize = max(dwSize, lpwfx->cbSize + sizeof(WAVEFORMATEX));

// !!! This doesn't really map to the supported formats of the filter.
// We should be using a property page based on IAMStreamConfig

// Put up a dialog box initialized with the current format
if (lpwfx = (LPWAVEFORMATEX)GlobalAllocPtr(GHND, dwSize)) {
CopyMemory(lpwfx, pmt->pbFormat, pmt->cbFormat);
_fmemset(&cfmt, 0, sizeof(ACMFORMATCHOOSE));
cfmt.cbStruct = sizeof(ACMFORMATCHOOSE);
cfmt.fdwStyle = ACMFORMATCHOOSE_STYLEF_INITTOWFXSTRUCT;
// show only formats we can capture
cfmt.fdwEnum = ACM_FORMATENUMF_HARDWARE | ACM_FORMATENUMF_INPUT;
cfmt.hwndOwner = ghwndApp;
cfmt.pwfx = lpwfx;
cfmt.cbwfx = dwSize;

// we chose a new format... so give it to the capture filter
if (!acmFormatChoose(&cfmt)) {
if (gcap.fPreviewing)
StopPreview(); // can't call IAMStreamConfig::SetFormat
// while streaming
((CMediaType *)pmt)->SetFormat((LPBYTE)lpwfx,
lpwfx->cbSize + sizeof(WAVEFORMATEX));
gcap.pASC->SetFormat(pmt); // filter will reconnect
if (gcap.fWantPreview)
StartPreview();
}
GlobalFreePtr(lpwfx) ;
}
DeleteMediaType(pmt);
}


/*----------------------------------------------------------------------------*\
| AppCommand()
|
| Process all of our WM_COMMAND messages.
\*----------------------------------------------------------------------------*/
LONG PASCAL AppCommand (HWND hwnd, unsigned msg, WPARAM wParam, LPARAM lParam)
{
HRESULT hr;
int id = GET_WM_COMMAND_ID(wParam, lParam);
switch(id)
{
//
// Our about box
//
case MENU_ABOUT:
DialogBox(ghInstApp, MAKEINTRESOURCE(IDD_ABOUT), hwnd,
(DLGPROC)AboutDlgProc);
break;

//
// We want out of here!
//
case MENU_EXIT:
PostMessage(hwnd,WM_CLOSE,0,0L);
break;

// choose a capture file
//
case MENU_SET_CAP_FILE:
SetCaptureFile(hwnd);
break;

// pre-allocate the capture file
//
case MENU_ALLOC_CAP_FILE:
AllocCaptureFile(hwnd);
break;

// save the capture file
//
case MENU_SAVE_CAP_FILE:
SaveCaptureFile(hwnd);
break;

// start capturing
//
case MENU_START_CAP:
if (gcap.fPreviewing)
StopPreview();
if (gcap.fPreviewGraphBuilt)
TearDownGraph();
BuildCaptureGraph();
StartCapture();
break;

// toggle preview
//
case MENU_PREVIEW:
gcap.fWantPreview = !gcap.fWantPreview;
if (gcap.fWantPreview) {
BuildPreviewGraph();
StartPreview();
} else
StopPreview();
break;

// stop capture
//
case MENU_STOP_CAP:
StopCapture();
if (gcap.fWantPreview) {
BuildPreviewGraph();
StartPreview();
}
break;

// select the master stream
//
case MENU_NOMASTER:
gcap.iMasterStream = -1;
if (gcap.pConfigAviMux) {
hr = gcap.pConfigAviMux->SetMasterStream(gcap.iMasterStream);
if (hr != NOERROR)
ErrMsg("SetMasterStream failed!");
}
break;
case MENU_AUDIOMASTER:
gcap.iMasterStream = 1;
if (gcap.pConfigAviMux) {
hr = gcap.pConfigAviMux->SetMasterStream(gcap.iMasterStream);
if (hr != NOERROR)
ErrMsg("SetMasterStream failed!");
}
break;
case MENU_VIDEOMASTER:
gcap.iMasterStream = 0;
if (gcap.pConfigAviMux) {
hr = gcap.pConfigAviMux->SetMasterStream(gcap.iMasterStream);
if (hr != NOERROR)
ErrMsg("SetMasterStream failed!");
}
break;

// toggle capturing audio
case MENU_CAP_AUDIO:
if (gcap.fPreviewing)
StopPreview();
gcap.fCapAudio = !gcap.fCapAudio;
// when we capture we'll need a different graph now
if (gcap.fCaptureGraphBuilt || gcap.fPreviewGraphBuilt)
TearDownGraph();
if (gcap.fWantPreview) {
BuildPreviewGraph();
StartPreview();
}
break;

// choose the audio capture format
//
case MENU_AUDIOFORMAT:
ChooseAudioFormat();
break;

// pick a frame rate
//
case MENU_FRAMERATE:
ChooseFrameRate();
break;

// pick a time limit
//
case MENU_TIMELIMIT:
ChooseTimeLimit();
break;

// pick which video capture device to use
// pick which video capture device to use
//
case MENU_VDEVICE0:
case MENU_VDEVICE1:
case MENU_VDEVICE2:
case MENU_VDEVICE3:
case MENU_VDEVICE4:
case MENU_VDEVICE5:
case MENU_VDEVICE6:
case MENU_VDEVICE7:
case MENU_VDEVICE8:
case MENU_VDEVICE9:
ChooseDevices(id - MENU_VDEVICE0, gcap.iAudioDevice);
break;

// pick which audio capture device to use
//
case MENU_ADEVICE0:
case MENU_ADEVICE1:
case MENU_ADEVICE2:
case MENU_ADEVICE3:
case MENU_ADEVICE4:
case MENU_ADEVICE5:
case MENU_ADEVICE6:
case MENU_ADEVICE7:
case MENU_ADEVICE8:
case MENU_ADEVICE9:
ChooseDevices(gcap.iVideoDevice, id - MENU_ADEVICE0);
break;

// video format dialog
//
case MENU_DIALOG0:
case MENU_DIALOG1:
case MENU_DIALOG2:
case MENU_DIALOG3:
case MENU_DIALOG4:
case MENU_DIALOG5:
case MENU_DIALOG6:
case MENU_DIALOG7:
case MENU_DIALOG8:
case MENU_DIALOG9:

// they want the VfW format dialog
if (id - MENU_DIALOG0 == gcap.iFormatDialogPos) {
hr = gcap.pDlg->ShowDialog(VfwCaptureDialog_Format, ghwndApp);
// this dialog might not work while previewing
if (hr != S_OK && gcap.fWantPreview) {
StopPreview();
gcap.pDlg->ShowDialog(VfwCaptureDialog_Format, ghwndApp);
}
if (gcap.pVSC) {
AM_MEDIA_TYPE *pmt;
// current capture format
hr = gcap.pVSC->GetFormat(&pmt);
if (hr == NOERROR) {
// resize our window to the new capture size
ResizeWindow(HEADER(pmt->pbFormat)->biWidth,
HEADER(pmt->pbFormat)->biHeight);
DeleteMediaType(pmt);
}
}
if (gcap.fWantPreview)
StartPreview();
} else if (id - MENU_DIALOG0 == gcap.iSourceDialogPos) {
hr = gcap.pDlg->ShowDialog(VfwCaptureDialog_Source, ghwndApp);
// this dialog might not work while previewing
if (hr != S_OK && gcap.fWantPreview) {
StopPreview();
gcap.pDlg->ShowDialog(VfwCaptureDialog_Source, ghwndApp);
}
if (gcap.fWantPreview)
StartPreview();
} else if (id - MENU_DIALOG0 == gcap.iDisplayDialogPos) {
hr = gcap.pDlg->ShowDialog(VfwCaptureDialog_Display, ghwndApp);
// this dialog might not work while previewing
if (hr != S_OK && gcap.fWantPreview) {
StopPreview();
gcap.pDlg->ShowDialog(VfwCaptureDialog_Display, ghwndApp);
}
if (gcap.fWantPreview)
StartPreview();

// now the code for the new dialogs

} else if (id - MENU_DIALOG0 == gcap.iVCapDialogPos) {
ISpecifyPropertyPages *pSpec;
CAUUID cauuid;
hr = gcap.pVCap->QueryInterface(IID_ISpecifyPropertyPages,
(void **)&pSpec);
if (hr == S_OK) {
hr = pSpec->GetPages(&cauuid);
hr = OleCreatePropertyFrame(ghwndApp, 30, 30, NULL, 1,
(IUnknown **)&gcap.pVCap, cauuid.cElems,
(GUID *)cauuid.pElems, 0, 0, NULL);
CoTaskMemFree(cauuid.pElems);
pSpec->Release();
}

} else if (id - MENU_DIALOG0 == gcap.iVCapCapturePinDialogPos) {
IAMStreamConfig *pSC;
hr = gcap.pBuilder->FindInterface(&PIN_CATEGORY_CAPTURE,
gcap.pVCap, IID_IAMStreamConfig, (void **)&pSC);
ISpecifyPropertyPages *pSpec;
CAUUID cauuid;
hr = pSC->QueryInterface(IID_ISpecifyPropertyPages,
(void **)&pSpec);
if (hr == S_OK) {
hr = pSpec->GetPages(&cauuid);
hr = OleCreatePropertyFrame(ghwndApp, 30, 30, NULL, 1,
(IUnknown **)&pSC, cauuid.cElems,
(GUID *)cauuid.pElems, 0, 0, NULL);
CoTaskMemFree(cauuid.pElems);
pSpec->Release();
}
pSC->Release();

} else if (id - MENU_DIALOG0 == gcap.iVCapPreviewPinDialogPos) {
IAMStreamConfig *pSC;
hr = gcap.pBuilder->FindInterface(&PIN_CATEGORY_PREVIEW,
gcap.pVCap, IID_IAMStreamConfig, (void **)&pSC);
ISpecifyPropertyPages *pSpec;
CAUUID cauuid;
hr = pSC->QueryInterface(IID_ISpecifyPropertyPages,
(void **)&pSpec);
if (hr == S_OK) {
hr = pSpec->GetPages(&cauuid);
hr = OleCreatePropertyFrame(ghwndApp, 30, 30, NULL, 1,
(IUnknown **)&pSC, cauuid.cElems,
(GUID *)cauuid.pElems, 0, 0, NULL);
CoTaskMemFree(cauuid.pElems);
pSpec->Release();
}
pSC->Release();

} else if (id - MENU_DIALOG0 == gcap.iVCrossbarDialogPos) {
IAMCrossbar *pX;
hr = gcap.pBuilder->FindInterface(&PIN_CATEGORY_CAPTURE,
gcap.pVCap, IID_IAMCrossbar, (void **)&pX);
ISpecifyPropertyPages *pSpec;
CAUUID cauuid;
hr = pX->QueryInterface(IID_ISpecifyPropertyPages,
(void **)&pSpec);
if (hr == S_OK) {
hr = pSpec->GetPages(&cauuid);
hr = OleCreatePropertyFrame(ghwndApp, 30, 30, NULL, 1,
(IUnknown **)&pX, cauuid.cElems,
(GUID *)cauuid.pElems, 0, 0, NULL);
CoTaskMemFree(cauuid.pElems);
pSpec->Release();
}
pX->Release();

} else if (id - MENU_DIALOG0 == gcap.iTVTunerDialogPos) {
IAMTVTuner *pTV;
hr = gcap.pBuilder->FindInterface(&PIN_CATEGORY_CAPTURE,
gcap.pVCap, IID_IAMTVTuner, (void **)&pTV);
ISpecifyPropertyPages *pSpec;
CAUUID cauuid;
hr = pTV->QueryInterface(IID_ISpecifyPropertyPages,
(void **)&pSpec);
if (hr == S_OK) {
hr = pSpec->GetPages(&cauuid);
hr = OleCreatePropertyFrame(ghwndApp, 30, 30, NULL, 1,
(IUnknown **)&pTV, cauuid.cElems,
(GUID *)cauuid.pElems, 0, 0, NULL);
CoTaskMemFree(cauuid.pElems);
pSpec->Release();
}
pTV->Release();

} else if (id - MENU_DIALOG0 == gcap.iACapDialogPos) {
ISpecifyPropertyPages *pSpec;
CAUUID cauuid;
hr = gcap.pACap->QueryInterface(IID_ISpecifyPropertyPages,
(void **)&pSpec);
if (hr == S_OK) {
hr = pSpec->GetPages(&cauuid);
hr = OleCreatePropertyFrame(ghwndApp, 30, 30, NULL, 1,
(IUnknown **)&gcap.pACap, cauuid.cElems,
(GUID *)cauuid.pElems, 0, 0, NULL);
CoTaskMemFree(cauuid.pElems);
pSpec->Release();
}

} else if (id - MENU_DIALOG0 == gcap.iACapCapturePinDialogPos) {
IAMStreamConfig *pSC;
hr = gcap.pBuilder->FindInterface(&PIN_CATEGORY_CAPTURE,
gcap.pACap, IID_IAMStreamConfig, (void **)&pSC);
ISpecifyPropertyPages *pSpec;
CAUUID cauuid;
hr = pSC->QueryInterface(IID_ISpecifyPropertyPages,
(void **)&pSpec);
if (hr == S_OK) {
hr = pSpec->GetPages(&cauuid);
hr = OleCreatePropertyFrame(ghwndApp, 30, 30, NULL, 1,
(IUnknown **)&pSC, cauuid.cElems,
(GUID *)cauuid.pElems, 0, 0, NULL);
CoTaskMemFree(cauuid.pElems);
pSpec->Release();
}
pSC->Release();

} else if (id - MENU_DIALOG0 == gcap.iACrossbarDialogPos) {
IAMCrossbar *pX;
hr = gcap.pBuilder->FindInterface(&PIN_CATEGORY_CAPTURE,
gcap.pACap, IID_IAMCrossbar, (void **)&pX);
ISpecifyPropertyPages *pSpec;
CAUUID cauuid;
hr = pX->QueryInterface(IID_ISpecifyPropertyPages,
(void **)&pSpec);
if (hr == S_OK) {
hr = pSpec->GetPages(&cauuid);
hr = OleCreatePropertyFrame(ghwndApp, 30, 30, NULL, 1,
(IUnknown **)&pX, cauuid.cElems,
(GUID *)cauuid.pElems, 0, 0, NULL);
CoTaskMemFree(cauuid.pElems);
pSpec->Release();
}
pX->Release();

} else if (id - MENU_DIALOG0 == gcap.iTVAudioDialogPos) {
IAMTVAudio *pTVA;
hr = gcap.pBuilder->FindInterface(&PIN_CATEGORY_CAPTURE,
gcap.pACap, IID_IAMTVAudio, (void **)&pTVA);
ISpecifyPropertyPages *pSpec;
CAUUID cauuid;
hr = pTVA->QueryInterface(IID_ISpecifyPropertyPages,
(void **)&pSpec);
if (hr == S_OK) {
hr = pSpec->GetPages(&cauuid);
hr = OleCreatePropertyFrame(ghwndApp, 30, 30, NULL, 1,
(IUnknown **)&pTVA, cauuid.cElems,
(GUID *)cauuid.pElems, 0, 0, NULL);
CoTaskMemFree(cauuid.pElems);
pSpec->Release();
}
pTVA->Release();
}

break;

}
return 0L;
}


/*----------------------------------------------------------------------------*\
| ErrMsg - Opens a Message box with a error message in it. The user can |
| select the OK button to continue |
\*----------------------------------------------------------------------------*/
int ErrMsg (LPTSTR sz,...)
{
static TCHAR ach[2000];
va_list va;

va_start(va, sz);
wvsprintf (ach,sz, va);
va_end(va);
MessageBox(ghwndApp,ach,NULL, MB_OK|MB_ICONEXCLAMATION|MB_TASKMODAL);
return FALSE;
}


/* AboutDlgProc()
*
* Dialog Procedure for the "about" dialog box.
*
*/

BOOL CALLBACK AboutDlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg) {
case WM_COMMAND:
EndDialog(hwnd, TRUE);
return TRUE;
case WM_INITDIALOG:
return TRUE;
}
return FALSE;
}


// pre-allocate the capture file
//
BOOL AllocCaptureFile(HWND hWnd)
{
// we'll get into an infinite loop in the dlg proc setting a value
if (gcap.szCaptureFile[0] == 0)
return FALSE;

/*
* show the allocate file space dialog to encourage
* the user to pre-allocate space
*/
if (DoDialog(hWnd, IDD_AllocCapFileSpace, (DLGPROC)AllocCapFileProc, 0)) {

// ensure repaint after dismissing dialog before
// possibly lengthy operation
UpdateWindow(ghwndApp);

// User has hit OK. Alloc requested capture file space
BOOL f = MakeBuilder();
if (!f)
return FALSE;
WCHAR wach[_MAX_PATH];
MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, gcap.szCaptureFile, -1,
wach, _MAX_PATH);
if (gcap.pBuilder->AllocCapFile(wach,
(DWORDLONG)gcap.wCapFileSize * 1024L * 1024L) != NOERROR) {
MessageBoxA(ghwndApp, "Error",
"Failed to pre-allocate capture file space",
MB_OK | MB_ICONEXCLAMATION);
return FALSE;
}
return TRUE;
} else {
return FALSE;
}
}


/*
* Put up the open file dialog
*/
BOOL OpenFileDialog(HWND hWnd, LPSTR lpName, int cb)
{
OPENFILENAMEA ofn;
LPSTR p;
char achFileName[_MAX_PATH];
char achBuffer[_MAX_PATH] ;

if (lpName == NULL || cb <= 0)
return FALSE;

// start with capture file as current file name
achFileName[0] = 0;
lstrcpy(achFileName, gcap.szCaptureFile);

// Get just the path info
// Terminate the full path at the last backslash
lstrcpy(achBuffer, achFileName);
for (p = achBuffer + lstrlen(achBuffer); p > achBuffer; p--) {
if (*p == '\\') {
*(p+1) = '\0';
break;
}
}

_fmemset(&ofn, 0, sizeof(OPENFILENAME)) ;
ofn.lStructSize = sizeof(OPENFILENAME) ;
ofn.hwndOwner = hWnd ;
ofn.lpstrFilter = "Microsoft AVI\0*.avi\0\0";
ofn.nFilterIndex = 0 ;
ofn.lpstrFile = achFileName;
ofn.nMaxFile = sizeof(achFileName) ;
ofn.lpstrFileTitle = NULL;
ofn.lpstrTitle = "Set Capture File";
ofn.nMaxFileTitle = 0 ;
ofn.lpstrInitialDir = achBuffer;
ofn.Flags = OFN_HIDEREADONLY | OFN_NOREADONLYRETURN | OFN_PATHMUSTEXIST ;

if (GetOpenFileNameA(&ofn)) {
// We have a capture file name
lstrcpyn(lpName, achFileName, cb);
return TRUE;
} else {
return FALSE;
}
}


/*
* Put up a dialog to allow the user to select a capture file.
*/
BOOL SetCaptureFile(HWND hWnd)
{
if (OpenFileDialog(hWnd, gcap.szCaptureFile, _MAX_PATH)) {
OFSTRUCT os;

// We have a capture file name

/*
* if this is a new file, then invite the user to
* allocate some space
*/
if (OpenFile(gcap.szCaptureFile, &os, OF_EXIST) == HFILE_ERROR) {

// bring up dialog, and set new file size
BOOL f = AllocCaptureFile(hWnd);
if (!f)
return FALSE;
}
} else {
return FALSE;
}

SetAppCaption(); // new a new app caption

// tell the file writer to use the new filename
if (gcap.pSink) {
WCHAR wach[_MAX_PATH];
MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, gcap.szCaptureFile, -1,
wach, _MAX_PATH);
gcap.pSink->SetFileName(wach, NULL);
}

return TRUE;
}


/*
* Put up a dialog to allow the user to save the contents of the capture file
* elsewhere
*/
BOOL SaveCaptureFile(HWND hWnd)
{
HRESULT hr;
char achDstFile[_MAX_PATH];
WCHAR wachDstFile[_MAX_PATH];
WCHAR wachSrcFile[_MAX_PATH];

if (gcap.pBuilder == NULL)
return FALSE;

if (OpenFileDialog(hWnd, achDstFile, _MAX_PATH)) {

// We have a capture file name
MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, gcap.szCaptureFile, -1,
wachSrcFile, _MAX_PATH);
MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, achDstFile, -1,
wachDstFile, _MAX_PATH);
statusUpdateStatus(ghwndStatus, "Saving capture file - please wait...");

// we need our own graph builder because the main one might not exist
ICaptureGraphBuilder *pBuilder;
hr = CoCreateInstance((REFCLSID)CLSID_CaptureGraphBuilder,
NULL, CLSCTX_INPROC, (REFIID)IID_ICaptureGraphBuilder,
(void **)&pBuilder);
if (hr == NOERROR) {
// allow the user to press ESC to abort... don't ask for progress
hr = pBuilder->CopyCaptureFile(wachSrcFile, wachDstFile,TRUE,NULL);
pBuilder->Release();
}
if (hr == S_OK)
statusUpdateStatus(ghwndStatus, "Capture file saved");
else if (hr == S_FALSE)
statusUpdateStatus(ghwndStatus, "Capture file save aborted");
else
statusUpdateStatus(ghwndStatus, "Capture file save ERROR");
return (hr == NOERROR ? TRUE : FALSE);

} else {
return TRUE; // they cancelled or something
}
}

// brings up a dialog box
//
int DoDialog(HWND hwndParent, int DialogID, DLGPROC fnDialog, long lParam)
{
DLGPROC fn;
int result;

fn = (DLGPROC)MakeProcInstance(fnDialog, ghInstApp);
result = DialogBoxParam(
ghInstApp,
MAKEINTRESOURCE(DialogID),
hwndParent,
fn,
lParam);
FreeProcInstance(fn);

return result;
}


//
// GetFreeDiskSpace: Function to Measure Available Disk Space
//
static long GetFreeDiskSpaceInKB(LPSTR pFile)
{
DWORD dwFreeClusters, dwBytesPerSector, dwSectorsPerCluster, dwClusters;
char RootName[MAX_PATH];
LPSTR ptmp; //required arg
ULARGE_INTEGER ulA, ulB, ulFreeBytes;

// need to find path for root directory on drive containing
// this file.

GetFullPathName(pFile, sizeof(RootName), RootName, &ptmp);

// truncate this to the name of the root directory (god how tedious)
if (RootName[0] == '\\' && RootName[1] == '\\') {

// path begins with \\server\share\path so skip the first
// three backslashes
ptmp = &RootName[2];
while (*ptmp && (*ptmp != '\\')) {
ptmp++;
}
if (*ptmp) {
// advance past the third backslash
ptmp++;
}
} else {
// path must be drv:\path
ptmp = RootName;
}

// find next backslash and put a null after it
while (*ptmp && (*ptmp != '\\')) {
ptmp++;
}
// found a backslash ?
if (*ptmp) {
// skip it and insert null
ptmp++;
*ptmp = '\0';
}

// the only real way of finding out free disk space is calling
// GetDiskFreeSpaceExA, but it doesn't exist on Win95

HINSTANCE h = LoadLibraryA("kernel32.dll");
if (h) {
typedef BOOL (WINAPI *MyFunc)(LPCSTR RootName, PULARGE_INTEGER pulA, PULARGE_INTEGER pulB, PULARGE_INTEGER pulFreeBytes);
MyFunc pfnGetDiskFreeSpaceEx = (MyFunc)GetProcAddress(h,
"GetDiskFreeSpaceExA");
FreeLibrary(h);
if (pfnGetDiskFreeSpaceEx) {
if (!pfnGetDiskFreeSpaceEx(RootName, &ulA, &ulB, &ulFreeBytes))
return -1;
return (long)(ulFreeBytes.QuadPart / 1024);
}
}

if (!GetDiskFreeSpace(RootName, &dwSectorsPerCluster, &dwBytesPerSector,
&dwFreeClusters, &dwClusters))
return (-1);
return(MulDiv(dwSectorsPerCluster * dwBytesPerSector,
dwFreeClusters,
1024));
}



// AllocCapFileProc: Capture file Space Allocation Dialog Box Procedure
//
int FAR PASCAL AllocCapFileProc(HWND hDlg, UINT Message, UINT wParam, LONG lParam)
{
static int nFreeMBs = 0 ;

switch (Message) {
case WM_INITDIALOG:
{
DWORDLONG dwlFileSize = 0;
long lFreeSpaceInKB;

// Get current capture file name and measure its size
dwlFileSize = GetSize(gcap.szCaptureFile);

// Get free disk space and add current capture file size to that.
// Convert the available space to MBs.
if ((lFreeSpaceInKB =
GetFreeDiskSpaceInKB(gcap.szCaptureFile)) != -1L) {
lFreeSpaceInKB += (long)(dwlFileSize / 1024);
nFreeMBs = lFreeSpaceInKB / 1024 ;
SetDlgItemInt(hDlg, IDD_SetCapFileFree, nFreeMBs, TRUE) ;
} else {
EnableWindow(GetDlgItem(hDlg, IDD_SetCapFileFree), FALSE);
}

gcap.wCapFileSize = (WORD) (dwlFileSize / (1024L * 1024L));

SetDlgItemInt(hDlg, IDD_SetCapFileSize, gcap.wCapFileSize, TRUE) ;
return TRUE ;
}

case WM_COMMAND :
switch (GET_WM_COMMAND_ID(wParam, lParam)) {
case IDOK :
{
int iCapFileSize ;

iCapFileSize = (int) GetDlgItemInt(hDlg, IDD_SetCapFileSize, NULL, TRUE) ;
if (iCapFileSize <= 0 || iCapFileSize > nFreeMBs) {
// You are asking for more than we have !! Sorry, ...
SetDlgItemInt(hDlg, IDD_SetCapFileSize, iCapFileSize, TRUE) ;
SetFocus(GetDlgItem(hDlg, IDD_SetCapFileSize)) ;
MessageBeep(MB_ICONEXCLAMATION) ;
return FALSE ;
}
gcap.wCapFileSize = iCapFileSize ;

EndDialog(hDlg, TRUE) ;
return TRUE ;
}

case IDCANCEL :
EndDialog(hDlg, FALSE) ;
return TRUE ;

case IDD_SetCapFileSize:
{
long l;
BOOL bchanged;
char achBuffer[21];

// check that entered size is a valid number
GetDlgItemText(hDlg, IDD_SetCapFileSize, achBuffer, sizeof(achBuffer));
l = atol(achBuffer);
bchanged = FALSE;
if (l < 1) {
l = 1;
bchanged = TRUE;
// don't infinite loop if there's < 1 Meg free
} else if (l > nFreeMBs && nFreeMBs > 0) {
l = nFreeMBs;
bchanged = TRUE;
} else {
// make sure there are no non-digit chars
// atol() will ignore trailing non-digit characters
int c = 0;
while (achBuffer[c]) {

if (IsCharAlpha(achBuffer[c]) || 
!IsCharAlphaNumeric(achBuffer[c])) {

// string contains non-digit chars - reset
l = 1;
bchanged = TRUE;
break;
}
c++;
}
}
if (bchanged) {
wsprintf(achBuffer, "%ld", l);
SetDlgItemText(hDlg, IDD_SetCapFileSize, achBuffer);
}
break;
}
}
break ;
}

return FALSE ;
}


//
// FrameRateProc: Choose a frame rate
//
int FAR PASCAL FrameRateProc(HWND hwnd, UINT msg, UINT wParam, LONG lParam)
{
char ach[32];

switch (msg) {
case WM_INITDIALOG:
/* put the current frame rate in the box */
sprintf(ach, "%f", gcap.FrameRate, ach);
SetDlgItemText(hwnd, IDC_FRAMERATE, ach);
break;

case WM_COMMAND:
switch(wParam){
case IDCANCEL:
EndDialog(hwnd, FALSE);
break;

case IDOK:
/* get the new frame rate */
GetDlgItemText(hwnd, IDC_FRAMERATE, ach, sizeof(ach));
if (atof(ach) <= 0.) {
ErrMsg("Invalid frame rate.");
break;
}
gcap.FrameRate = atof(ach);
EndDialog(hwnd, TRUE);
break;
}
break;

default:
return FALSE;
}
return TRUE;
}


//
// TimeLimitProc: Choose a capture time limit
//
int FAR PASCAL TimeLimitProc(HWND hwnd, UINT msg, UINT wParam, LONG lParam)
{
char ach[32];
DWORD dwTimeLimit;

switch (msg) {
case WM_INITDIALOG:
/* put the current time limit info in the boxes */
sprintf(ach, "%d", gcap.dwTimeLimit, ach);
SetDlgItemText(hwnd, IDC_TIMELIMIT, ach);
CheckDlgButton(hwnd, IDC_USETIMELIMIT, gcap.fUseTimeLimit);
break;

case WM_COMMAND:
switch(wParam){
case IDCANCEL:
EndDialog(hwnd, FALSE);
break;

case IDOK:
/* get the new time limit */
dwTimeLimit = GetDlgItemInt(hwnd, IDC_TIMELIMIT, NULL, FALSE);
gcap.dwTimeLimit = dwTimeLimit;
gcap.fUseTimeLimit = IsDlgButtonChecked(hwnd, IDC_USETIMELIMIT);
EndDialog(hwnd, TRUE);
break;
}
break;

default:
return FALSE;
}
return TRUE;
}


//
// PressAKeyProc: Press OK to capture
//
int FAR PASCAL PressAKeyProc(HWND hwnd, UINT msg, UINT wParam, LONG lParam)
{
char ach[_MAX_PATH];

switch (msg) {
case WM_INITDIALOG:
/* set the current file name in the box */
wsprintf(ach, "%s", gcap.szCaptureFile);
SetDlgItemText(hwnd, IDC_CAPFILENAME, ach);
break;

case WM_COMMAND:
switch(wParam){
case IDCANCEL:
EndDialog(hwnd, FALSE);
break;

case IDOK:
EndDialog(hwnd, TRUE);
break;
}
break;

default:
return FALSE;
}
return TRUE;
}

DWORDLONG GetSize(LPCSTR ach)
{
HANDLE hFile = CreateFileA(ach, GENERIC_READ, FILE_SHARE_READ, 0,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);

if (hFile == INVALID_HANDLE_VALUE) {
return 0;
}

DWORD dwSizeHigh;
DWORD dwSizeLow = GetFileSize(hFile, &dwSizeHigh);

DWORDLONG dwlSize = dwSizeLow + ((DWORDLONG)dwSizeHigh << 32);

if (!CloseHandle(hFile)) {
dwlSize = 0;
}

return dwlSize;
}