home *** CD-ROM | disk | FTP | other *** search
/ Visual Basic Game Programming for Teens / VBGPFT.cdr / DirectX8 / dx8a_sdk.exe / samples / multimedia / directshow / capture / amcap / amcap.cpp next >
Encoding:
C/C++ Source or Header  |  2000-11-04  |  119.0 KB  |  3,771 lines

  1. //------------------------------------------------------------------------------
  2. // File: AMCap.cpp
  3. //
  4. // Desc: Audio/Video Capture sample for DirectShow
  5. //
  6. // Copyright (c) 1993 - 2000, Microsoft Corporation.  All rights reserved.
  7. //------------------------------------------------------------------------------
  8.  
  9. #include <windows.h>
  10. #include <dbt.h>
  11. #include <streams.h>
  12. #include <mmreg.h>
  13. #include <msacm.h>
  14. #include <fcntl.h>
  15. #include <io.h>
  16. #include <stdio.h>
  17. #include <commdlg.h>
  18. #include "stdafx.h"
  19. #include "amcap.h"
  20. #include "status.h"
  21. #include "crossbar.h"
  22.  
  23. // you can never have too many parentheses!
  24. #define ABS(x) (((x) > 0) ? (x) : -(x))
  25.  
  26. //------------------------------------------------------------------------------
  27. // Global data
  28. //------------------------------------------------------------------------------
  29. HINSTANCE ghInstApp;
  30. HACCEL ghAccel;
  31. HFONT  ghfontApp;
  32. TEXTMETRIC gtm;
  33. TCHAR gszAppName[]=TEXT("AMCAP");
  34. HWND ghwndApp, ghwndStatus;
  35. HDEVNOTIFY ghDevNotify;
  36. PUnregisterDeviceNotification gpUnregisterDeviceNotification;
  37. PRegisterDeviceNotification gpRegisterDeviceNotification;
  38. struct _capstuff {
  39.     TCHAR szCaptureFile[_MAX_PATH];
  40.     WORD wCapFileSize;  // size in Meg
  41.     ICaptureGraphBuilder2 *pBuilder;
  42.     IVideoWindow *pVW;
  43.     IMediaEventEx *pME;
  44.     IAMDroppedFrames *pDF;
  45.     IAMVideoCompression *pVC;
  46.     IAMVfwCaptureDialogs *pDlg;
  47.     IAMStreamConfig *pASC;      // for audio cap
  48.     IAMStreamConfig *pVSC;      // for video cap
  49.     IBaseFilter *pRender;
  50.     IBaseFilter *pVCap, *pACap;
  51.     IGraphBuilder *pFg;
  52.     IFileSinkFilter *pSink;
  53.     IConfigAviMux *pConfigAviMux;
  54.     int  iMasterStream;
  55.     BOOL fCaptureGraphBuilt;
  56.     BOOL fPreviewGraphBuilt;
  57.     BOOL fCapturing;
  58.     BOOL fPreviewing;
  59.     BOOL fCapAudio;
  60.     BOOL fCapCC;
  61.     BOOL fCCAvail;
  62.     BOOL fCapAudioIsRelevant;
  63.     bool fDeviceMenuPopulated;
  64.     IMoniker *rgpmVideoMenu[10];
  65.     IMoniker *rgpmAudioMenu[10];
  66.     IMoniker *pmVideo;
  67.     IMoniker *pmAudio;
  68.     double FrameRate;
  69.     BOOL fWantPreview;
  70.     long lCapStartTime;
  71.     long lCapStopTime;
  72.     WCHAR wachFriendlyName[120];
  73.     BOOL fUseTimeLimit;
  74.     BOOL fUseFrameRate;
  75.     DWORD dwTimeLimit;
  76.     int iFormatDialogPos;
  77.     int iSourceDialogPos;
  78.     int iDisplayDialogPos;
  79.     int iVCapDialogPos;
  80.     int iVCrossbarDialogPos;
  81.     int iTVTunerDialogPos;
  82.     int iACapDialogPos;
  83.     int iACrossbarDialogPos;
  84.     int iTVAudioDialogPos;
  85.     int iVCapCapturePinDialogPos;
  86.     int iVCapPreviewPinDialogPos;
  87.     int iACapCapturePinDialogPos;
  88.     long lDroppedBase;
  89.     long lNotBase;
  90.     BOOL fPreviewFaked;
  91.     CCrossbar *pCrossbar;
  92.     int iVideoInputMenuPos;
  93.     LONG NumberOfVideoInputs;
  94.     HMENU hMenuPopup;
  95.     int iNumVCapDevices;
  96. } gcap;
  97.  
  98.  
  99. // implements IAMCopyCaptureFileProgress
  100. //
  101. class CProgress : public CUnknown, public IAMCopyCaptureFileProgress
  102. {
  103. public:
  104.  
  105.     CProgress(TCHAR *pName, LPUNKNOWN pUnk, HRESULT *phr) :
  106.         CUnknown(pName, pUnk, phr) {};
  107.     ~CProgress() {};
  108.  
  109.     STDMETHODIMP_(ULONG) AddRef() {return 1;};
  110.     STDMETHODIMP_(ULONG) Release() {return 0;};
  111.     STDMETHODIMP QueryInterface(REFIID iid, void **p) {
  112.         CheckPointer(p, E_POINTER);
  113.         if (iid == IID_IAMCopyCaptureFileProgress) {
  114.             return GetInterface((IAMCopyCaptureFileProgress *)this, p);
  115.         } else {
  116.             return E_NOINTERFACE;
  117.         }
  118.     };
  119.     STDMETHODIMP Progress(int i) {
  120.         TCHAR tach[80];
  121.         wsprintf(tach, TEXT("Save File Progress: %d%%"), i);
  122.         statusUpdateStatus(ghwndStatus, tach);
  123.         return S_OK;
  124.     };
  125. };
  126.  
  127. CComModule _Module;
  128.  
  129. //------------------------------------------------------------------------------
  130. // Funciton Prototypes
  131. //------------------------------------------------------------------------------
  132. typedef LONG (PASCAL *LPWNDPROC)(HWND, UINT, WPARAM, LPARAM); // pointer to a window procedure
  133. LONG WINAPI AppWndProc(HWND hwnd, UINT uiMessage, WPARAM wParam, LPARAM lParam);
  134. LONG PASCAL AppCommand(HWND hwnd, unsigned msg, WPARAM wParam, LPARAM lParam);
  135. BOOL CALLBACK AboutDlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
  136. int ErrMsg (LPTSTR sz,...);
  137. BOOL SetCaptureFile(HWND hWnd);
  138. BOOL SaveCaptureFile(HWND hWnd);
  139. BOOL AllocCaptureFile(HWND hWnd);
  140. int DoDialog(HWND hwndParent, int DialogID, DLGPROC fnDialog, long lParam);
  141. int FAR PASCAL AllocCapFileProc(HWND hDlg, UINT Message, UINT wParam, LONG lParam);
  142. int FAR PASCAL FrameRateProc(HWND hDlg, UINT Message, UINT wParam, LONG lParam);
  143. int FAR PASCAL TimeLimitProc(HWND hDlg, UINT Message, UINT wParam, LONG lParam);
  144. int FAR PASCAL PressAKeyProc(HWND hDlg, UINT Message, UINT wParam, LONG lParam);
  145. void TearDownGraph(void);
  146. BOOL BuildCaptureGraph();
  147. BOOL BuildPreviewGraph();
  148. void UpdateStatus(BOOL fAllStats);
  149. void AddDevicesToMenu();
  150. void ChooseDevices(char *szVideo, char *szAudio);
  151. void ChooseDevices(IMoniker *pmVideo, IMoniker *pmAudio);
  152. void ChooseFrameRate();
  153. BOOL InitCapFilters();
  154. void FreeCapFilters();
  155. BOOL StopPreview();
  156. BOOL StartPreview();
  157. BOOL StopCapture();
  158. DWORDLONG GetSize(LPCTSTR tach);
  159. void MakeMenuOptions();
  160.  
  161.  
  162. //------------------------------------------------------------------------------
  163. // Name: SetAppCaption()
  164. // Desc: Set the caption to be the application name followed by the capture file
  165. //------------------------------------------------------------------------------
  166. void SetAppCaption()
  167. {
  168.     TCHAR tach[_MAX_PATH + 80];
  169.     lstrcpy(tach, gszAppName);
  170.     if (gcap.szCaptureFile[0] != 0) {
  171.     lstrcat(tach, " - ");
  172.     lstrcat(tach, gcap.szCaptureFile);
  173.     }
  174.     SetWindowText(ghwndApp, tach);
  175. }
  176.  
  177.  
  178. /*----------------------------------------------------------------------------*\
  179. |   AppInit( hInst, hPrev)                                                     |
  180. |                                                                              |
  181. |   Description:                                                               |
  182. |       This is called when the application is first loaded into               |
  183. |       memory.  It performs all initialization that doesn't need to be done   |
  184. |       once per instance.                                                     |
  185. |                                                                              |
  186. |   Arguments:                                                                 |
  187. |       hInstance       instance handle of current instance                    |
  188. |       hPrev           instance handle of previous instance                   |
  189. |                                                                              |
  190. |   Returns:                                                                   |
  191. |       TRUE if successful, FALSE if not                                       |
  192. |                                                                              |
  193. \*----------------------------------------------------------------------------*/
  194. BOOL AppInit(HINSTANCE hInst, HINSTANCE hPrev, int sw)
  195. {
  196.     WNDCLASS    cls;
  197.     HDC         hdc;
  198.  
  199.     const DWORD  dwExStyle = 0;
  200.  
  201.     CoInitialize(NULL);
  202.     DbgInitialise(hInst);
  203.  
  204.     /* Save instance handle for DialogBoxs */
  205.     ghInstApp = hInst;
  206.  
  207.     ghAccel = LoadAccelerators(hInst, MAKEINTATOM(ID_APP));
  208.  
  209.     if (!hPrev) {
  210.     /*
  211.      *  Register a class for the main application window
  212.      */
  213.     cls.hCursor        = LoadCursor(NULL,IDC_ARROW);
  214.     cls.hIcon          = LoadIcon(hInst, TEXT("AMCapIcon"));
  215.     cls.lpszMenuName   = MAKEINTATOM(ID_APP);
  216.     cls.lpszClassName  = MAKEINTATOM(ID_APP);
  217.     cls.hbrBackground  = (HBRUSH)(COLOR_WINDOW + 1);
  218.     cls.hInstance      = hInst;
  219.     cls.style          = CS_BYTEALIGNCLIENT | CS_VREDRAW | CS_HREDRAW | CS_DBLCLKS;
  220.     cls.lpfnWndProc    = (LPWNDPROC)AppWndProc;
  221.     cls.cbWndExtra     = 0;
  222.     cls.cbClsExtra     = 0;
  223.  
  224.     if (!RegisterClass(&cls))
  225.         return FALSE;
  226.     }
  227.  
  228.     // Is this necessary?
  229.     ghfontApp = (HFONT)GetStockObject(ANSI_VAR_FONT);
  230.     hdc = GetDC(NULL);
  231.     SelectObject(hdc, ghfontApp);
  232.     GetTextMetrics(hdc, >m);
  233.     ReleaseDC(NULL, hdc);
  234.  
  235.     ghwndApp=CreateWindowEx(dwExStyle,
  236.                 MAKEINTATOM(ID_APP),    // Class name
  237.                 gszAppName,             // Caption
  238.                             // Style bits
  239.                 WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
  240.                 CW_USEDEFAULT, 0,       // Position
  241.                 320,300,                // Size
  242.                 (HWND)NULL,             // Parent window (no parent)
  243.                 (HMENU)NULL,            // use class menu
  244.                 hInst,                  // handle to window instance
  245.                 (LPSTR)NULL             // no params to pass on
  246.                );
  247.  
  248.     // create the status bar
  249.     statusInit(hInst, hPrev);
  250.     ghwndStatus = CreateWindowEx(
  251.             0,
  252.             szStatusClass,
  253.             NULL,
  254.             WS_CHILD|WS_BORDER|WS_VISIBLE|WS_CLIPSIBLINGS,
  255.             0, 0,
  256.             0, 0,
  257.             ghwndApp,
  258.             NULL,
  259.             hInst,
  260.             NULL);
  261.     if (ghwndStatus == NULL) {
  262.     return(FALSE);
  263.     }
  264.     ShowWindow(ghwndApp,sw);
  265.  
  266.     // get the capture file name from win.ini
  267.     GetProfileString(TEXT("annie"), TEXT("CaptureFile"), TEXT(""),
  268.                                         gcap.szCaptureFile,
  269.                     sizeof(gcap.szCaptureFile));
  270.  
  271.     // get which devices to use from win.ini
  272.     ZeroMemory(gcap.rgpmAudioMenu, sizeof(gcap.rgpmAudioMenu));
  273.     ZeroMemory(gcap.rgpmVideoMenu, sizeof(gcap.rgpmVideoMenu));
  274.     gcap.pmVideo = 0;
  275.     gcap.pmAudio = 0;
  276.  
  277.     TCHAR szVideoDisplayName[1024], szAudioDisplayName[1024];
  278.     *szAudioDisplayName = *szVideoDisplayName = 0; // null terminate
  279.     
  280.     GetProfileString(TEXT("annie"), TEXT("VideoDevice2"), TEXT(""),
  281.                     szVideoDisplayName,
  282.                     sizeof(szVideoDisplayName));
  283.     GetProfileString(TEXT("annie"), TEXT("AudioDevice2"), TEXT(""),
  284.                     szAudioDisplayName,
  285.                     sizeof(szAudioDisplayName));
  286.  
  287.     gcap.fDeviceMenuPopulated = false;
  288.     AddDevicesToMenu();
  289.  
  290.     // do we want audio?
  291.     gcap.fCapAudio = GetProfileInt(TEXT("annie"), TEXT("CaptureAudio"), TRUE);
  292.     gcap.fCapCC = GetProfileInt(TEXT("annie"), TEXT("CaptureCC"), FALSE);
  293.  
  294.     // do we want preview?
  295.     gcap.fWantPreview = GetProfileInt(TEXT("annie"), TEXT("WantPreview"), FALSE);
  296.     // which stream should be the master? NONE(-1) means nothing special happens
  297.     // AUDIO(1) means the video frame rate is changed before written out to keep
  298.     // the movie in sync when the audio and video capture crystals are not the
  299.     // same (and therefore drift out of sync after a few minutes).  VIDEO(0)
  300.     // means the audio sample rate is changed before written out
  301.     gcap.iMasterStream = GetProfileInt(TEXT("annie"), TEXT("MasterStream"), 1);
  302.  
  303.  
  304.     // get the frame rate from win.ini before making the graph
  305.     gcap.fUseFrameRate = GetProfileInt(TEXT("annie"), TEXT("UseFrameRate"), 1);
  306.     int units_per_frame = GetProfileInt(TEXT("annie"), TEXT("FrameRate"), 666667);  // 15fps
  307.     gcap.FrameRate = 10000000. / units_per_frame;
  308.     gcap.FrameRate = (int)(gcap.FrameRate * 100) / 100.;
  309.     // reasonable default
  310.     if (gcap.FrameRate <= 0.)
  311.     gcap.FrameRate = 15.0;
  312.      
  313.     gcap.fUseTimeLimit = GetProfileInt(TEXT("annie"), TEXT("UseTimeLimit"), 0);
  314.     gcap.dwTimeLimit = GetProfileInt(TEXT("annie"), TEXT("TimeLimit"), 0);
  315.  
  316.     // instantiate the capture filters we need to do the menu items
  317.     // this will start previewing, if wanted
  318.     // 
  319.     // make these the official devices we're using
  320.     
  321.     ChooseDevices(szVideoDisplayName, szAudioDisplayName);
  322.     // and builds a partial filtergraph.
  323.  
  324.     // Register for device add/remove notifications.
  325.     DEV_BROADCAST_DEVICEINTERFACE filterData;
  326.     ZeroMemory(&filterData, sizeof(DEV_BROADCAST_DEVICEINTERFACE));
  327.     
  328.     filterData.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE);
  329.     filterData.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
  330.     filterData.dbcc_classguid = AM_KSCATEGORY_CAPTURE;
  331.  
  332.     gpUnregisterDeviceNotification = NULL;
  333.     gpRegisterDeviceNotification = NULL;
  334.     // dynload device removal APIs
  335.     {
  336.         HMODULE hmodUser = GetModuleHandle(TEXT("user32.dll"));
  337.         ASSERT(hmodUser);       // we link to user32
  338.         gpUnregisterDeviceNotification = (PUnregisterDeviceNotification)
  339.             GetProcAddress(hmodUser, "UnregisterDeviceNotification");
  340.  
  341.         // m_pRegisterDeviceNotification is prototyped differently in unicode
  342.         gpRegisterDeviceNotification = (PRegisterDeviceNotification)
  343.             GetProcAddress(hmodUser,
  344. #ifdef UNICODE
  345.                            "RegisterDeviceNotificationW"
  346. #else
  347.                            "RegisterDeviceNotificationA"
  348. #endif
  349.                            );
  350.         // failures expected on older platforms.
  351.         ASSERT(gpRegisterDeviceNotification && gpUnregisterDeviceNotification ||
  352.                !gpRegisterDeviceNotification && !gpUnregisterDeviceNotification);
  353.     }
  354.  
  355.     ghDevNotify = NULL;
  356.  
  357.     if (gpRegisterDeviceNotification)
  358.     {
  359.         ghDevNotify = gpRegisterDeviceNotification(ghwndApp, &filterData, DEVICE_NOTIFY_WINDOW_HANDLE);
  360.         ASSERT(ghDevNotify != NULL);
  361.     }
  362.  
  363.     SetAppCaption();
  364.     return TRUE;
  365. }
  366.  
  367. void IMonRelease(IMoniker *&pm) {
  368.     if(pm) {
  369.         pm->Release();
  370.         pm = 0;
  371.     }
  372. }
  373.  
  374. /*----------------------------------------------------------------------------*\
  375. |   WinMain( hInst, hPrev, lpszCmdLine, cmdShow )                              |
  376. |                                                                              |
  377. |   Description:                                                               |
  378. |       The main procedure for the App.  After initializing, it just goes      |
  379. |       into a message-processing loop until it gets a WM_QUIT message         |
  380. |       (meaning the app was closed).                                          |
  381. |                                                                              |
  382. |   Arguments:                                                                 |
  383. |       hInst           instance handle of this instance of the app            |
  384. |       hPrev           instance handle of previous instance, NULL if first    |
  385. |       szCmdLine       ->null-terminated command line                         |
  386. |       cmdShow         specifies how the window is initially displayed        |
  387. |                                                                              |
  388. |   Returns:                                                                   |
  389. |       The exit code as specified in the WM_QUIT message.                     |
  390. |                                                                              |
  391. \*----------------------------------------------------------------------------*/
  392. int PASCAL WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR szCmdLine, int sw)
  393. {
  394.     MSG     msg;
  395.  
  396.     /* Call initialization procedure */
  397.     if (!AppInit(hInst,hPrev,sw))
  398.     return FALSE;
  399.  
  400.     /*
  401.      * Polling messages from event queue
  402.      */
  403.     for (;;)
  404.     {
  405.     while (PeekMessage(&msg, NULL, 0, 0,PM_REMOVE))
  406.     {
  407.         if (msg.message == WM_QUIT)
  408.         return msg.wParam;
  409.  
  410.         if (TranslateAccelerator(ghwndApp, ghAccel, &msg))
  411.         continue;
  412.     
  413.         TranslateMessage(&msg);
  414.         DispatchMessage(&msg);
  415.     }
  416.  
  417.     WaitMessage();
  418.     }
  419.  
  420.     // not reached
  421.     return msg.wParam;
  422. }
  423.  
  424.  
  425. /*----------------------------------------------------------------------------*\
  426. |   AppWndProc( hwnd, uiMessage, wParam, lParam )                              |
  427. |                                                                              |
  428. |   Description:                                                               |
  429. |       The window proc for the app's main (tiled) window.  This processes all |
  430. |       of the parent window's messages.                                       |
  431. |                                                                              |
  432. |   Arguments:                                                                 |
  433. |       hwnd            window handle for the window                           |
  434. |       msg             message number                                         |
  435. |       wParam          message-dependent                                      |
  436. |       lParam          message-dependent                                      |
  437. |                                                                              |
  438. |   Returns:                                                                   |
  439. |       0 if processed, nonzero if ignored                                     |
  440. |                                                                              |
  441. \*----------------------------------------------------------------------------*/
  442. LONG WINAPI  AppWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
  443. {
  444.     PAINTSTRUCT ps;
  445.     HDC hdc;
  446.     RECT rc;
  447.     int cxBorder, cyBorder, cy;
  448.  
  449.     switch (msg) {
  450.  
  451.     //
  452.     //
  453.     case WM_CREATE:
  454.         break;
  455.  
  456.     case WM_COMMAND:
  457.         return AppCommand(hwnd,msg,wParam,lParam);
  458.  
  459.     case WM_INITMENU:
  460.         // we can start capture if not capturing already
  461.         EnableMenuItem((HMENU)wParam, MENU_START_CAP, 
  462.             (!gcap.fCapturing) ? MF_ENABLED :
  463.             MF_GRAYED);
  464.         // we can stop capture if it's currently capturing
  465.         EnableMenuItem((HMENU)wParam, MENU_STOP_CAP, 
  466.             (gcap.fCapturing) ? MF_ENABLED : MF_GRAYED);
  467.  
  468.         // We can bring up a dialog if the graph is stopped
  469.         EnableMenuItem((HMENU)wParam, MENU_DIALOG0, !gcap.fCapturing ?
  470.         MF_ENABLED : MF_GRAYED);
  471.         EnableMenuItem((HMENU)wParam, MENU_DIALOG1, !gcap.fCapturing ?
  472.         MF_ENABLED : MF_GRAYED);
  473.         EnableMenuItem((HMENU)wParam, MENU_DIALOG2, !gcap.fCapturing ?
  474.         MF_ENABLED : MF_GRAYED);
  475.         EnableMenuItem((HMENU)wParam, MENU_DIALOG3, !gcap.fCapturing ?
  476.         MF_ENABLED : MF_GRAYED);
  477.         EnableMenuItem((HMENU)wParam, MENU_DIALOG4, !gcap.fCapturing ?
  478.         MF_ENABLED : MF_GRAYED);
  479.         EnableMenuItem((HMENU)wParam, MENU_DIALOG5, !gcap.fCapturing ?
  480.         MF_ENABLED : MF_GRAYED);
  481.         EnableMenuItem((HMENU)wParam, MENU_DIALOG6, !gcap.fCapturing ?
  482.         MF_ENABLED : MF_GRAYED);
  483.         EnableMenuItem((HMENU)wParam, MENU_DIALOG7, !gcap.fCapturing ?
  484.         MF_ENABLED : MF_GRAYED);
  485.         EnableMenuItem((HMENU)wParam, MENU_DIALOG8, !gcap.fCapturing ?
  486.         MF_ENABLED : MF_GRAYED);
  487.         EnableMenuItem((HMENU)wParam, MENU_DIALOG9, !gcap.fCapturing ?
  488.         MF_ENABLED : MF_GRAYED);
  489.         EnableMenuItem((HMENU)wParam, MENU_DIALOGA, !gcap.fCapturing ?
  490.         MF_ENABLED : MF_GRAYED);
  491.         EnableMenuItem((HMENU)wParam, MENU_DIALOGB, !gcap.fCapturing ?
  492.         MF_ENABLED : MF_GRAYED);
  493.         EnableMenuItem((HMENU)wParam, MENU_DIALOGC, !gcap.fCapturing ?
  494.         MF_ENABLED : MF_GRAYED);
  495.         EnableMenuItem((HMENU)wParam, MENU_DIALOGD, !gcap.fCapturing ?
  496.         MF_ENABLED : MF_GRAYED);
  497.         EnableMenuItem((HMENU)wParam, MENU_DIALOGE, !gcap.fCapturing ?
  498.         MF_ENABLED : MF_GRAYED);
  499.         EnableMenuItem((HMENU)wParam, MENU_DIALOGF, !gcap.fCapturing ?
  500.         MF_ENABLED : MF_GRAYED);
  501.  
  502.         // toggles capturing audio or not - can't be capturing right now
  503.         // and we must have an audio capture device created
  504.         EnableMenuItem((HMENU)wParam, MENU_CAP_AUDIO, 
  505.             (!gcap.fCapturing && gcap.pACap) ? MF_ENABLED : MF_GRAYED);
  506.         // are we capturing audio?
  507.         CheckMenuItem((HMENU)wParam, MENU_CAP_AUDIO, 
  508.             (gcap.fCapAudio) ? MF_CHECKED : MF_UNCHECKED);
  509.         // are we doing closed captioning?
  510.         CheckMenuItem((HMENU)wParam, MENU_CAP_CC, 
  511.             (gcap.fCapCC) ? MF_CHECKED : MF_UNCHECKED);
  512.         EnableMenuItem((HMENU)wParam, MENU_CAP_CC, 
  513.             (gcap.fCCAvail) ? MF_ENABLED : MF_GRAYED);
  514.         // change audio formats when not capturing
  515.         EnableMenuItem((HMENU)wParam, MENU_AUDIOFORMAT, (gcap.fCapAudio &&
  516.             !gcap.fCapturing) ? MF_ENABLED : MF_GRAYED);
  517.         // change frame rate when not capturing, and only if the video
  518.         // filter captures a VIDEOINFO type format
  519.         EnableMenuItem((HMENU)wParam, MENU_FRAMERATE,
  520.             (!gcap.fCapturing && gcap.fCapAudioIsRelevant) ?
  521.              MF_ENABLED : MF_GRAYED);
  522.         // change time limit when not capturing
  523.         EnableMenuItem((HMENU)wParam, MENU_TIMELIMIT,
  524.             !gcap.fCapturing ? MF_ENABLED : MF_GRAYED);
  525.         // change capture file name when not capturing
  526.         EnableMenuItem((HMENU)wParam, MENU_SET_CAP_FILE,
  527.             !gcap.fCapturing ? MF_ENABLED : MF_GRAYED);
  528.         // pre-allocate capture file when not capturing
  529.         EnableMenuItem((HMENU)wParam, MENU_ALLOC_CAP_FILE,
  530.             !gcap.fCapturing ? MF_ENABLED : MF_GRAYED);
  531.         // can save capture file when not capturing
  532.         EnableMenuItem((HMENU)wParam, MENU_SAVE_CAP_FILE,
  533.             !gcap.fCapturing ? MF_ENABLED : MF_GRAYED);
  534.         // do we want preview?
  535.         CheckMenuItem((HMENU)wParam, MENU_PREVIEW, 
  536.             (gcap.fWantPreview) ? MF_CHECKED : MF_UNCHECKED);
  537.         // can toggle preview if not capturing
  538.         EnableMenuItem((HMENU)wParam, MENU_PREVIEW,
  539.             !gcap.fCapturing ? MF_ENABLED : MF_GRAYED);
  540.  
  541.         // which is the master stream? Not applicable unless we're also
  542.         // capturing audio
  543.         EnableMenuItem((HMENU)wParam, MENU_NOMASTER,
  544.         (!gcap.fCapturing && gcap.fCapAudio) ? MF_ENABLED : MF_GRAYED);
  545.         CheckMenuItem((HMENU)wParam, MENU_NOMASTER, 
  546.             (gcap.iMasterStream == -1) ? MF_CHECKED : MF_UNCHECKED);
  547.         EnableMenuItem((HMENU)wParam, MENU_AUDIOMASTER,
  548.         (!gcap.fCapturing && gcap.fCapAudio) ? MF_ENABLED : MF_GRAYED);
  549.         CheckMenuItem((HMENU)wParam, MENU_AUDIOMASTER, 
  550.             (gcap.iMasterStream == 1) ? MF_CHECKED : MF_UNCHECKED);
  551.         EnableMenuItem((HMENU)wParam, MENU_VIDEOMASTER,
  552.         (!gcap.fCapturing && gcap.fCapAudio) ? MF_ENABLED : MF_GRAYED);
  553.         CheckMenuItem((HMENU)wParam, MENU_VIDEOMASTER, 
  554.             (gcap.iMasterStream == 0) ? MF_CHECKED : MF_UNCHECKED);
  555.  
  556.         // can't select a new capture device when capturing
  557.         EnableMenuItem((HMENU)wParam, MENU_VDEVICE0,
  558.             !gcap.fCapturing ? MF_ENABLED : MF_GRAYED);
  559.         EnableMenuItem((HMENU)wParam, MENU_VDEVICE1,
  560.             !gcap.fCapturing ? MF_ENABLED : MF_GRAYED);
  561.         EnableMenuItem((HMENU)wParam, MENU_VDEVICE2,
  562.             !gcap.fCapturing ? MF_ENABLED : MF_GRAYED);
  563.         EnableMenuItem((HMENU)wParam, MENU_VDEVICE3,
  564.             !gcap.fCapturing ? MF_ENABLED : MF_GRAYED);
  565.         EnableMenuItem((HMENU)wParam, MENU_VDEVICE4,
  566.             !gcap.fCapturing ? MF_ENABLED : MF_GRAYED);
  567.         EnableMenuItem((HMENU)wParam, MENU_VDEVICE5,
  568.             !gcap.fCapturing ? MF_ENABLED : MF_GRAYED);
  569.         EnableMenuItem((HMENU)wParam, MENU_VDEVICE6,
  570.             !gcap.fCapturing ? MF_ENABLED : MF_GRAYED);
  571.         EnableMenuItem((HMENU)wParam, MENU_VDEVICE7,
  572.             !gcap.fCapturing ? MF_ENABLED : MF_GRAYED);
  573.         EnableMenuItem((HMENU)wParam, MENU_VDEVICE8,
  574.             !gcap.fCapturing ? MF_ENABLED : MF_GRAYED);
  575.         EnableMenuItem((HMENU)wParam, MENU_VDEVICE9,
  576.             !gcap.fCapturing ? MF_ENABLED : MF_GRAYED);
  577.         EnableMenuItem((HMENU)wParam, MENU_ADEVICE0,
  578.             !gcap.fCapturing ? MF_ENABLED : MF_GRAYED);
  579.         EnableMenuItem((HMENU)wParam, MENU_ADEVICE1,
  580.             !gcap.fCapturing ? MF_ENABLED : MF_GRAYED);
  581.         EnableMenuItem((HMENU)wParam, MENU_ADEVICE2,
  582.             !gcap.fCapturing ? MF_ENABLED : MF_GRAYED);
  583.         EnableMenuItem((HMENU)wParam, MENU_ADEVICE3,
  584.             !gcap.fCapturing ? MF_ENABLED : MF_GRAYED);
  585.         EnableMenuItem((HMENU)wParam, MENU_ADEVICE4,
  586.             !gcap.fCapturing ? MF_ENABLED : MF_GRAYED);
  587.         EnableMenuItem((HMENU)wParam, MENU_ADEVICE5,
  588.             !gcap.fCapturing ? MF_ENABLED : MF_GRAYED);
  589.         EnableMenuItem((HMENU)wParam, MENU_ADEVICE6,
  590.             !gcap.fCapturing ? MF_ENABLED : MF_GRAYED);
  591.         EnableMenuItem((HMENU)wParam, MENU_ADEVICE7,
  592.             !gcap.fCapturing ? MF_ENABLED : MF_GRAYED);
  593.         EnableMenuItem((HMENU)wParam, MENU_ADEVICE8,
  594.             !gcap.fCapturing ? MF_ENABLED : MF_GRAYED);
  595.         EnableMenuItem((HMENU)wParam, MENU_ADEVICE9,
  596.             !gcap.fCapturing ? MF_ENABLED : MF_GRAYED);
  597.         
  598.         break;
  599.  
  600.  
  601.       case WM_INITMENUPOPUP:
  602.             if(GetSubMenu(GetMenu(ghwndApp), 1) == (HMENU)wParam) {
  603.                 AddDevicesToMenu();
  604.             }
  605.         
  606.             break;
  607.     //
  608.     // We're out of here!
  609.     //
  610.     case WM_DESTROY:
  611.         DbgTerminate();
  612.  
  613.         IMonRelease(gcap.pmVideo);
  614.         IMonRelease(gcap.pmAudio);
  615.         {
  616.             for(int i = 0; i < NUMELMS(gcap.rgpmVideoMenu); i++) {
  617.                 IMonRelease(gcap.rgpmVideoMenu[i]);
  618.             }
  619.             for( i = 0; i < NUMELMS(gcap.rgpmAudioMenu); i++) {
  620.                 IMonRelease(gcap.rgpmAudioMenu[i]);
  621.             }
  622.         }
  623.  
  624.         CoUninitialize();
  625.         PostQuitMessage(0);
  626.         break;
  627.  
  628.     //
  629.     //
  630.     case WM_CLOSE:
  631.         // Unregister device notifications
  632.         if (ghDevNotify != NULL)
  633.         {
  634.             ASSERT(gpUnregisterDeviceNotification);
  635.             gpUnregisterDeviceNotification(ghDevNotify);
  636.             ghDevNotify = NULL;
  637.         }
  638.  
  639.         StopPreview();
  640.         StopCapture();
  641.         TearDownGraph();
  642.         FreeCapFilters();
  643.  
  644.         // store current settings in win.ini for next time
  645.         WriteProfileString(TEXT("annie"), TEXT("CaptureFile"),
  646.                                                 gcap.szCaptureFile);
  647.             
  648.         TCHAR tach[120];
  649.         WCHAR *szDisplayName;
  650.         szDisplayName = 0;
  651.         wsprintf(tach, TEXT("%S"), L"");
  652.         if(gcap.pmVideo) 
  653.         {
  654.             if (SUCCEEDED(gcap.pmVideo->GetDisplayName(0, 0, &szDisplayName)))
  655.             {
  656.                 wsprintf(tach, TEXT("%S"), szDisplayName ? szDisplayName : L"");
  657.                 CoTaskMemFree(szDisplayName);
  658.             }
  659.         }
  660.         WriteProfileString(TEXT("annie"), TEXT("VideoDevice2"), tach);
  661.         wsprintf(tach, TEXT("%S"), L"");
  662.         szDisplayName = 0;
  663.         if(gcap.pmAudio) 
  664.         {
  665.             if (SUCCEEDED(gcap.pmAudio->GetDisplayName(0, 0, &szDisplayName)))
  666.             {
  667.                 wsprintf(tach, TEXT("%S"), szDisplayName ? szDisplayName : L"");
  668.                 CoTaskMemFree(szDisplayName);
  669.             }
  670.         }
  671.         WriteProfileString(TEXT("annie"), TEXT("AudioDevice2"), tach);
  672.             
  673.         wsprintf(tach, TEXT("%d"), (int)(10000000 / gcap.FrameRate));
  674.         WriteProfileString(TEXT("annie"), TEXT("FrameRate"), tach);
  675.         wsprintf(tach, TEXT("%d"), gcap.fUseFrameRate);
  676.         WriteProfileString(TEXT("annie"), TEXT("UseFrameRate"), tach);
  677.         wsprintf(tach, TEXT("%d"), gcap.fCapAudio);
  678.         WriteProfileString(TEXT("annie"), TEXT("CaptureAudio"), tach);
  679.         wsprintf(tach, TEXT("%d"), gcap.fCapCC);
  680.         WriteProfileString(TEXT("annie"), TEXT("CaptureCC"), tach);
  681.         wsprintf(tach, TEXT("%d"), gcap.fWantPreview);
  682.         WriteProfileString(TEXT("annie"), TEXT("WantPreview"), tach);
  683.         wsprintf(tach, TEXT("%d"), gcap.iMasterStream);
  684.         WriteProfileString(TEXT("annie"), TEXT("MasterStream"), tach);
  685.         wsprintf(tach, TEXT("%d"), gcap.fUseTimeLimit);
  686.         WriteProfileString(TEXT("annie"), TEXT("UseTimeLimit"), tach);
  687.         wsprintf(tach, TEXT("%d"), gcap.dwTimeLimit);
  688.         WriteProfileString(TEXT("annie"), TEXT("TimeLimit"), tach);
  689.  
  690.         break;
  691.  
  692.     case WM_ERASEBKGND:
  693.         break;
  694.  
  695.     // ESC will stop capture
  696.     case WM_KEYDOWN:
  697.         if ((GetAsyncKeyState(VK_ESCAPE) & 0x01) && gcap.fCapturing) {
  698.         StopCapture();
  699.         if (gcap.fWantPreview) {
  700.             BuildPreviewGraph();
  701.             StartPreview();
  702.         }
  703.         }
  704.         break;
  705.         
  706.     case WM_PAINT:
  707.         hdc = BeginPaint(hwnd,&ps);
  708.  
  709.         // nothing to do
  710.  
  711.         EndPaint(hwnd,&ps);
  712.         break;
  713.  
  714.     case WM_TIMER:
  715.         // update our status bar with #captured, #dropped
  716.         // if we've stopped capturing, don't do it anymore.  Some WM_TIMER
  717.         // messages may come late, after we've destroyed the graph and
  718.         // we'll get invalid numbers.
  719.         if (gcap.fCapturing)
  720.             UpdateStatus(FALSE);
  721.  
  722.         // is our time limit up?
  723.         if (gcap.fUseTimeLimit) {
  724.         if ((timeGetTime() - gcap.lCapStartTime) / 1000 >=
  725.                             gcap.dwTimeLimit) {
  726.             StopCapture();
  727.             if (gcap.fWantPreview) {
  728.             BuildPreviewGraph();
  729.             StartPreview();
  730.             }
  731.         }
  732.         }
  733.         break;
  734.  
  735.     case WM_SIZE:
  736.         // make the preview window fit inside our window, taking up
  737.         // all of our client area except for the status window at the
  738.         // bottom
  739.         GetClientRect(ghwndApp, &rc);
  740.         cxBorder = GetSystemMetrics(SM_CXBORDER);
  741.         cyBorder = GetSystemMetrics(SM_CYBORDER);
  742.         cy = statusGetHeight() + cyBorder;
  743.         MoveWindow(ghwndStatus, -cxBorder, rc.bottom - cy,
  744.             rc.right + (2 * cxBorder), cy + cyBorder, TRUE);
  745.         rc.bottom -= cy;
  746.         // this is the video renderer window showing the preview
  747.         if (gcap.pVW)
  748.         gcap.pVW->SetWindowPosition(0, 0, rc.right, rc.bottom);
  749.         break;
  750.  
  751.     case WM_FGNOTIFY:
  752.         // uh-oh, something went wrong while capturing - the filtergraph
  753.         // will send us events like EC_COMPLETE, EC_USERABORT and the one
  754.         // we care about, EC_ERRORABORT.
  755.         if (gcap.pME) {
  756.             LONG event, l1, l2;
  757.             BOOL bAbort = FALSE;
  758.             while (gcap.pME->GetEvent(&event, &l1, &l2, 0) == S_OK) 
  759.             {
  760.                 gcap.pME->FreeEventParams(event, l1, l2);
  761.                 if (event == EC_ERRORABORT) {
  762.                     StopCapture();
  763.                     bAbort = TRUE;
  764.                     continue;
  765.                 }
  766.                 else if (event == EC_DEVICE_LOST)
  767.                 {
  768.                     // Check if we have lost a capture filter being used.
  769.                     // lParam2 of EC_DEVICE_LOST event == 1 indicates device added
  770.                     //                                   == 0 indicates device removed
  771.                     if (l2 == 0)
  772.                     {
  773.                         IBaseFilter *pf;
  774.                         IUnknown *punk = (IUnknown *) l1;
  775.                         if (S_OK == punk->QueryInterface(IID_IBaseFilter, (void **) &pf))
  776.                         {
  777.                             if (::IsEqualObject(gcap.pVCap, pf))
  778.                             {
  779.                                 pf->Release();
  780.                                 bAbort = FALSE;
  781.                                 StopCapture();
  782.                                 char szError[1000];
  783.                                 wsprintf(szError, "Stopping Capture (Device Lost). Select New Capture Device", l1);
  784.                                 ErrMsg(szError);
  785.                                 break;
  786.                             }
  787.                             pf->Release();
  788.                         }
  789.                     }
  790.                 }
  791.             } // end while
  792.             if (bAbort)
  793.             {
  794.                 if (gcap.fWantPreview) {
  795.                 BuildPreviewGraph();
  796.                 StartPreview();
  797.                 }
  798.                 char szError[1000];
  799.                 wsprintf(szError, "ERROR during capture, error code=%08x", l1);
  800.                 ErrMsg(szError);
  801.             }
  802.         }
  803.         break;
  804.     
  805.     case WM_DEVICECHANGE:
  806.         // We are interested in only device arrival & removal events
  807.         if (DBT_DEVICEARRIVAL != wParam && DBT_DEVICEREMOVECOMPLETE != wParam)
  808.             break;
  809.         PDEV_BROADCAST_HDR pdbh = (PDEV_BROADCAST_HDR) lParam;
  810.         if (pdbh->dbch_devicetype != DBT_DEVTYP_DEVICEINTERFACE)
  811.         {
  812.             break;
  813.         }
  814.         PDEV_BROADCAST_DEVICEINTERFACE pdbi = (PDEV_BROADCAST_DEVICEINTERFACE) lParam;
  815.         // Check for capture devices.
  816.         if (pdbi->dbcc_classguid != AM_KSCATEGORY_CAPTURE)
  817.         {
  818.             break;
  819.         }
  820.  
  821.         // Check for device arrival/removal.
  822.         if (DBT_DEVICEARRIVAL == wParam || DBT_DEVICEREMOVECOMPLETE == wParam)
  823.         {
  824.             gcap.fDeviceMenuPopulated = false;
  825.         }
  826.         break;
  827.  
  828.     }
  829.     return DefWindowProc(hwnd,msg,wParam,lParam);
  830. }
  831.  
  832.  
  833. // Make a graph builder object we can use for capture graph building
  834. //
  835. BOOL MakeBuilder()
  836. {
  837.     // we have one already
  838.     if (gcap.pBuilder)
  839.     return TRUE;
  840.  
  841.     HRESULT hr = CoCreateInstance((REFCLSID)CLSID_CaptureGraphBuilder2,
  842.             NULL, CLSCTX_INPROC, (REFIID)IID_ICaptureGraphBuilder2,
  843.             (void **)&gcap.pBuilder);
  844.     return (hr == NOERROR) ? TRUE : FALSE;
  845. }
  846.  
  847.  
  848. // Make a graph object we can use for capture graph building
  849. //
  850. BOOL MakeGraph()
  851. {
  852.     // we have one already
  853.     if (gcap.pFg)
  854.     return TRUE;
  855.  
  856.     HRESULT hr = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC,
  857.                    IID_IGraphBuilder, (LPVOID *)&gcap.pFg);
  858.     return (hr == NOERROR) ? TRUE : FALSE;
  859. }
  860.  
  861.  
  862. // make sure the preview window inside our window is as big as the
  863. // dimensions of captured video, or some capture cards won't show a preview.
  864. // (Also, it helps people tell what size video they're capturing)
  865. // We will resize our app's window big enough so that once the status bar
  866. // is positioned at the bottom there will be enough room for the preview
  867. // window to be w x h
  868. //
  869.  
  870. int gnRecurse = 0;
  871.  
  872. void ResizeWindow(int w, int h)
  873. {
  874.     RECT rcW, rcC;
  875.     int xExtra, yExtra;
  876.     int cyBorder = GetSystemMetrics(SM_CYBORDER);
  877.  
  878.     gnRecurse++;
  879.  
  880.     GetWindowRect(ghwndApp, &rcW);
  881.     GetClientRect(ghwndApp, &rcC);
  882.     xExtra = rcW.right - rcW.left - rcC.right;
  883.     yExtra = rcW.bottom - rcW.top - rcC.bottom + cyBorder + statusGetHeight();
  884.     
  885.     rcC.right = w;
  886.     rcC.bottom = h;
  887.     SetWindowPos(ghwndApp, NULL, 0, 0, rcC.right + xExtra,
  888.                 rcC.bottom + yExtra, SWP_NOZORDER | SWP_NOMOVE);
  889.  
  890.     // we may need to recurse once.  But more than that means the window cannot
  891.     // be made the size we want, trying will just stack fault.
  892.     //
  893.     if (gnRecurse == 1 && ((rcC.right + xExtra != rcW.right - rcW.left && w > GetSystemMetrics(SM_CXMIN)) ||
  894.         (rcC.bottom + yExtra != rcW.bottom - rcW.top)))
  895.     ResizeWindow(w,h);
  896.  
  897.     gnRecurse--;
  898. }
  899.  
  900.  
  901. // Tear down everything downstream of a given filter
  902. void NukeDownstream(IBaseFilter *pf)
  903. {
  904.     //DbgLog((LOG_TRACE,1,TEXT("Nuking...")));
  905.  
  906.     IPin *pP, *pTo;
  907.     ULONG u;
  908.     IEnumPins *pins = NULL;
  909.     PIN_INFO pininfo;
  910.     HRESULT hr = pf->EnumPins(&pins);
  911.     pins->Reset();
  912.     while (hr == NOERROR) {
  913.         hr = pins->Next(1, &pP, &u);
  914.     if (hr == S_OK && pP) {
  915.         pP->ConnectedTo(&pTo);
  916.         if (pTo) {
  917.             hr = pTo->QueryPinInfo(&pininfo);
  918.             if (hr == NOERROR) {
  919.             if (pininfo.dir == PINDIR_INPUT) {
  920.                 NukeDownstream(pininfo.pFilter);
  921.                 gcap.pFg->Disconnect(pTo);
  922.                 gcap.pFg->Disconnect(pP);
  923.                     gcap.pFg->RemoveFilter(pininfo.pFilter);
  924.             }
  925.                 pininfo.pFilter->Release();
  926.         }
  927.         pTo->Release();
  928.         }
  929.         pP->Release();
  930.     }
  931.     }
  932.     if (pins)
  933.         pins->Release();
  934. }
  935.  
  936.  
  937. // Tear down everything downstream of the capture filters, so we can build
  938. // a different capture graph.  Notice that we never destroy the capture filters
  939. // and WDM filters upstream of them, because then all the capture settings
  940. // we've set would be lost.
  941. //
  942. void TearDownGraph()
  943. {
  944.     if (gcap.pSink)
  945.     gcap.pSink->Release();
  946.     gcap.pSink = NULL;
  947.     if (gcap.pConfigAviMux)
  948.     gcap.pConfigAviMux->Release();
  949.     gcap.pConfigAviMux = NULL;
  950.     if (gcap.pRender)
  951.     gcap.pRender->Release();
  952.     gcap.pRender = NULL;
  953.     if (gcap.pVW) {
  954.     // stop drawing in our window, or we may get wierd repaint effects
  955.     gcap.pVW->put_Owner(NULL);
  956.     gcap.pVW->put_Visible(OAFALSE);
  957.     gcap.pVW->Release();
  958.     }
  959.     gcap.pVW = NULL;
  960.     if (gcap.pME)
  961.     gcap.pME->Release();
  962.     gcap.pME = NULL;
  963.     if (gcap.pDF)
  964.     gcap.pDF->Release();
  965.     gcap.pDF = NULL;
  966.  
  967.     // destroy the graph downstream of our capture filters
  968.     if (gcap.pVCap)
  969.     NukeDownstream(gcap.pVCap);
  970.     if (gcap.pACap)
  971.     NukeDownstream(gcap.pACap);
  972.  
  973.     // potential debug output - what the graph looks like
  974.     // if (gcap.pFg) DumpGraph(gcap.pFg, 1);
  975.  
  976.     gcap.fCaptureGraphBuilt = FALSE;
  977.     gcap.fPreviewGraphBuilt = FALSE;
  978.     gcap.fPreviewFaked = FALSE;
  979. }
  980.  
  981.  
  982. // create the capture filters of the graph.  We need to keep them loaded from
  983. // the beginning, so we can set parameters on them and have them remembered
  984. //
  985. BOOL InitCapFilters()
  986. {
  987.     HRESULT hr;
  988.     BOOL f;
  989.     UINT uIndex = 0;
  990.  
  991.     gcap.fCCAvail = FALSE;    // assume no closed captioning support
  992.  
  993.     f = MakeBuilder();
  994.     if (!f) {
  995.     ErrMsg("Cannot instantiate graph builder");
  996.     return FALSE;
  997.     }
  998.  
  999. //
  1000. // First, we need a Video Capture filter, and some interfaces
  1001. //
  1002.  
  1003.     gcap.pVCap = NULL;
  1004.     if(gcap.pmVideo != 0)
  1005.     {
  1006.         IPropertyBag *pBag;
  1007.         gcap.wachFriendlyName[0] = 0;
  1008.         hr = gcap.pmVideo->BindToStorage(0, 0, IID_IPropertyBag, (void **)&pBag);
  1009.         if(SUCCEEDED(hr))
  1010.         {
  1011.             VARIANT var;
  1012.             var.vt = VT_BSTR;
  1013.             hr = pBag->Read(L"FriendlyName", &var, NULL);
  1014.             if (hr == NOERROR) {
  1015.                 lstrcpyW(gcap.wachFriendlyName, var.bstrVal);
  1016.                 SysFreeString(var.bstrVal);
  1017.             }
  1018.             pBag->Release();
  1019.         }
  1020.         hr = gcap.pmVideo->BindToObject(0, 0, IID_IBaseFilter, (void**)&gcap.pVCap);
  1021.     }
  1022.  
  1023.     if (gcap.pVCap == NULL) {
  1024.     ErrMsg("Error %x: Cannot create video capture filter", hr);
  1025.     goto InitCapFiltersFail;
  1026.     }
  1027.  
  1028.     //
  1029.     // make a filtergraph, give it to the graph builder and put the video
  1030.     // capture filter in the graph
  1031.     //
  1032.  
  1033.     f = MakeGraph();
  1034.     if (!f) {
  1035.     ErrMsg("Cannot instantiate filtergraph");
  1036.     goto InitCapFiltersFail;
  1037.     }
  1038.     hr = gcap.pBuilder->SetFiltergraph(gcap.pFg);
  1039.     if (hr != NOERROR) {
  1040.     ErrMsg("Cannot give graph to builder");
  1041.     goto InitCapFiltersFail;
  1042.     }
  1043.  
  1044.     hr = gcap.pFg->AddFilter(gcap.pVCap, NULL);
  1045.     if (hr != NOERROR) {
  1046.     ErrMsg("Error %x: Cannot add vidcap to filtergraph", hr);
  1047.     goto InitCapFiltersFail;
  1048.     }
  1049.  
  1050.     // Calling FindInterface below will result in building the upstream
  1051.     // section of the capture graph (any WDM TVTuners or Crossbars we might
  1052.     // need).
  1053.  
  1054.     // we use this interface to get the name of the driver
  1055.     // Don't worry if it doesn't work:  This interface may not be available
  1056.     // until the pin is connected, or it may not be available at all.
  1057.     // (eg: interface may not be available for some DV capture)
  1058.     hr = gcap.pBuilder->FindInterface(&PIN_CATEGORY_CAPTURE,
  1059.             &MEDIATYPE_Interleaved,
  1060.             gcap.pVCap, IID_IAMVideoCompression, (void **)&gcap.pVC);
  1061.     if (hr != S_OK) {
  1062.         hr = gcap.pBuilder->FindInterface(&PIN_CATEGORY_CAPTURE,
  1063.             &MEDIATYPE_Video,
  1064.             gcap.pVCap, IID_IAMVideoCompression, (void **)&gcap.pVC);
  1065.     }
  1066.  
  1067.     // !!! What if this interface isn't supported?
  1068.     // we use this interface to set the frame rate and get the capture size
  1069.     hr = gcap.pBuilder->FindInterface(&PIN_CATEGORY_CAPTURE,
  1070.             &MEDIATYPE_Interleaved,
  1071.             gcap.pVCap, IID_IAMStreamConfig, (void **)&gcap.pVSC);
  1072.     if (hr != NOERROR) {
  1073.         hr = gcap.pBuilder->FindInterface(&PIN_CATEGORY_CAPTURE,
  1074.                 &MEDIATYPE_Video, gcap.pVCap,
  1075.                 IID_IAMStreamConfig, (void **)&gcap.pVSC);
  1076.         if (hr != NOERROR) {
  1077.         // this means we can't set frame rate (non-DV only)
  1078.         ErrMsg("Error %x: Cannot find VCapture:IAMStreamConfig", hr);
  1079.     }
  1080.     }
  1081.  
  1082.     gcap.fCapAudioIsRelevant = TRUE;
  1083.  
  1084.     AM_MEDIA_TYPE *pmt;
  1085.     // default capture format
  1086.     if (gcap.pVSC && gcap.pVSC->GetFormat(&pmt) == S_OK) {
  1087.         // DV capture does not use a VIDEOINFOHEADER
  1088.     if (pmt->formattype == FORMAT_VideoInfo) {
  1089.             // resize our window to the default capture size
  1090.             ResizeWindow(HEADER(pmt->pbFormat)->biWidth,
  1091.                 ABS(HEADER(pmt->pbFormat)->biHeight));
  1092.     }
  1093.     if (pmt->majortype != MEDIATYPE_Video) {
  1094.         // This capture filter captures something other that pure video.
  1095.         // Maybe it's DV or something?  Anyway, chances are we shouldn't
  1096.         // allow capturing audio separately, since our video capture    
  1097.         // filter may have audio combined in it already!
  1098.             gcap.fCapAudioIsRelevant = FALSE;
  1099.             gcap.fCapAudio = FALSE;
  1100.     }
  1101.         DeleteMediaType(pmt);
  1102.     }
  1103.  
  1104.     // we use this interface to bring up the 3 dialogs
  1105.     // NOTE:  Only the VfW capture filter supports this.  This app only brings
  1106.     // up dialogs for legacy VfW capture drivers, since only those have dialogs
  1107.     hr = gcap.pBuilder->FindInterface(&PIN_CATEGORY_CAPTURE,
  1108.                 &MEDIATYPE_Video, gcap.pVCap,
  1109.                 IID_IAMVfwCaptureDialogs, (void **)&gcap.pDlg);
  1110.  
  1111.  
  1112.     // Use the crossbar class to help us sort out all the possible video inputs
  1113.     // The class needs to be given the capture filters ANALOGVIDEO input pin
  1114.     {
  1115.         IPin        *pP = 0;
  1116.         IEnumPins   *pins;
  1117.         ULONG        n;
  1118.         PIN_INFO     pinInfo;
  1119.         BOOL         Found = FALSE;
  1120.     IKsPropertySet *pKs;
  1121.     GUID guid;
  1122.     DWORD dw;
  1123.     BOOL fMatch = FALSE;
  1124.         
  1125.         gcap.pCrossbar = NULL;
  1126.  
  1127.         if(SUCCEEDED(gcap.pVCap->EnumPins(&pins))) {            
  1128.             while (!Found && (S_OK == pins->Next(1, &pP, &n))) {
  1129.                 if (S_OK == pP->QueryPinInfo(&pinInfo)) {
  1130.                     if (pinInfo.dir == PINDIR_INPUT) {
  1131.             
  1132.             // is this pin an ANALOGVIDEOIN input pin?
  1133.                 if (pP->QueryInterface(IID_IKsPropertySet,
  1134.                         (void **)&pKs) == S_OK) {
  1135.                 if (pKs->Get(AMPROPSETID_Pin,
  1136.                     AMPROPERTY_PIN_CATEGORY, NULL, 0,
  1137.                     &guid, sizeof(GUID), &dw) == S_OK) {
  1138.                     if (guid == PIN_CATEGORY_ANALOGVIDEOIN)
  1139.                     fMatch = TRUE;
  1140.                 }
  1141.                 pKs->Release();
  1142.                 }
  1143.  
  1144.             if (fMatch) {
  1145.                             gcap.pCrossbar = new CCrossbar (pP);
  1146.                             hr = gcap.pCrossbar->GetInputCount
  1147.                         (&gcap.NumberOfVideoInputs);
  1148.                             Found = TRUE;
  1149.             }
  1150.                     }
  1151.                     pinInfo.pFilter->Release();
  1152.                 }
  1153.                 pP->Release();
  1154.             }
  1155.             pins->Release();
  1156.         }
  1157.     }
  1158.  
  1159.     // there's no point making an audio capture filter
  1160.     if (gcap.fCapAudioIsRelevant == FALSE)
  1161.     goto SkipAudio;
  1162.  
  1163. // create the audio capture filter, even if we are not capturing audio right
  1164. // now, so we have all the filters around all the time.
  1165.  
  1166.     //
  1167.     // We want an audio capture filter and some interfaces
  1168.     //
  1169.  
  1170.     if(gcap.pmAudio == 0)
  1171.     {
  1172.     // there are no audio capture devices. We'll only allow video capture
  1173.     gcap.fCapAudio = FALSE;
  1174.     goto SkipAudio;
  1175.     }
  1176.     gcap.pACap = NULL;
  1177.  
  1178.  
  1179.     hr = gcap.pmAudio->BindToObject(0, 0, IID_IBaseFilter, (void**)&gcap.pACap);
  1180.  
  1181.     if (gcap.pACap == NULL) {
  1182.     // there are no audio capture devices. We'll only allow video capture
  1183.     gcap.fCapAudio = FALSE;
  1184.     ErrMsg("Cannot create audio capture filter");
  1185.     goto SkipAudio;
  1186.     }
  1187.  
  1188.     //
  1189.     // put the audio capture filter in the graph
  1190.     //
  1191.  
  1192.     // We'll need this in the graph to get audio property pages
  1193.     hr = gcap.pFg->AddFilter(gcap.pACap, NULL);
  1194.     if (hr != NOERROR) {
  1195.         ErrMsg("Error %x: Cannot add audcap to filtergraph", hr);
  1196.         goto InitCapFiltersFail;
  1197.     }
  1198.  
  1199.     // Calling FindInterface below will result in building the upstream
  1200.     // section of the capture graph (any WDM TVAudio's or Crossbars we might
  1201.     // need).
  1202.  
  1203.     // !!! What if this interface isn't supported?
  1204.     // we use this interface to set the captured wave format
  1205.     hr = gcap.pBuilder->FindInterface(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Audio,
  1206.             gcap.pACap, IID_IAMStreamConfig, (void **)&gcap.pASC);
  1207.     if (hr != NOERROR) {
  1208.         ErrMsg("Cannot find ACapture:IAMStreamConfig");
  1209.     }
  1210.  
  1211. SkipAudio:
  1212.  
  1213.     // Can this filter do closed captioning?
  1214.     IPin *pPin;
  1215.     hr = gcap.pBuilder->FindPin(gcap.pVCap, PINDIR_OUTPUT, &PIN_CATEGORY_VBI,
  1216.                 NULL, FALSE, 0, &pPin);
  1217.     if (hr != S_OK)
  1218.         hr = gcap.pBuilder->FindPin(gcap.pVCap, PINDIR_OUTPUT, &PIN_CATEGORY_CC,
  1219.                 NULL, FALSE, 0, &pPin);
  1220.     if (hr == S_OK) {
  1221.     pPin->Release();
  1222.     gcap.fCCAvail = TRUE;
  1223.     } else {
  1224.     gcap.fCapCC = FALSE;    // can't capture it, then
  1225.     }
  1226.  
  1227.     // potential debug output - what the graph looks like
  1228.     // DumpGraph(gcap.pFg, 1);
  1229.  
  1230.     return TRUE;
  1231.  
  1232. InitCapFiltersFail:
  1233.     FreeCapFilters();
  1234.     return FALSE;
  1235. }
  1236.  
  1237.  
  1238. // all done with the capture filters and the graph builder
  1239. //
  1240. void FreeCapFilters()
  1241. {
  1242.     if (gcap.pFg)
  1243.     gcap.pFg->Release();
  1244.     gcap.pFg = NULL;
  1245.     if (gcap.pBuilder)
  1246.     gcap.pBuilder->Release();
  1247.     gcap.pBuilder = NULL;
  1248.     if (gcap.pVCap)
  1249.     gcap.pVCap->Release();
  1250.     gcap.pVCap = NULL;
  1251.     if (gcap.pACap)
  1252.     gcap.pACap->Release();
  1253.     gcap.pACap = NULL;
  1254.     if (gcap.pASC)
  1255.     gcap.pASC->Release();
  1256.     gcap.pASC = NULL;
  1257.     if (gcap.pVSC)
  1258.     gcap.pVSC->Release();
  1259.     gcap.pVSC = NULL;
  1260.     if (gcap.pVC)
  1261.     gcap.pVC->Release();
  1262.     gcap.pVC = NULL;
  1263.     if (gcap.pDlg)
  1264.     gcap.pDlg->Release();
  1265.     gcap.pDlg = NULL;
  1266.     if (gcap.pCrossbar) {
  1267.        delete gcap.pCrossbar;
  1268.        gcap.pCrossbar = NULL;
  1269.     }
  1270. }
  1271.  
  1272.  
  1273. // build the capture graph!
  1274. //
  1275. BOOL BuildCaptureGraph()
  1276. {
  1277.     USES_CONVERSION;
  1278.     int cy, cyBorder;
  1279.     HRESULT hr;
  1280.     BOOL f;
  1281.     AM_MEDIA_TYPE *pmt;
  1282.  
  1283.     // we have one already
  1284.     if (gcap.fCaptureGraphBuilt)
  1285.     return TRUE;
  1286.  
  1287.     // No rebuilding while we're running
  1288.     if (gcap.fCapturing || gcap.fPreviewing)
  1289.     return FALSE;
  1290.  
  1291.     // We don't have the necessary capture filters
  1292.     if (gcap.pVCap == NULL)
  1293.     return FALSE;
  1294.     if (gcap.pACap == NULL && gcap.fCapAudio)
  1295.     return FALSE;
  1296.  
  1297.     // no capture file name yet... we need one first
  1298.     if (gcap.szCaptureFile[0] == 0) {
  1299.     f = SetCaptureFile(ghwndApp);
  1300.     if (!f)
  1301.         return f;
  1302.     }
  1303.  
  1304.     // we already have another graph built... tear down the old one
  1305.     if (gcap.fPreviewGraphBuilt)
  1306.     TearDownGraph();
  1307.  
  1308. //
  1309. // We need a rendering section that will write the capture file out in AVI
  1310. // file format
  1311. //
  1312.  
  1313.     GUID guid = MEDIASUBTYPE_Avi;
  1314.     hr = gcap.pBuilder->SetOutputFileName(&guid, T2W(gcap.szCaptureFile),
  1315.                                                 &gcap.pRender, &gcap.pSink);
  1316.     if (hr != NOERROR) {
  1317.     ErrMsg("Cannot set output file");
  1318.     goto SetupCaptureFail;
  1319.     }
  1320.  
  1321. // Now tell the AVIMUX to write out AVI files that old apps can read properly.
  1322. // If we don't, most apps won't be able to tell where the keyframes are,
  1323. // slowing down editing considerably
  1324. // Doing this will cause one seek (over the area the index will go) when
  1325. // you capture past 1 Gig, but that's no big deal.
  1326. // NOTE: This is on by default, so it's not necessary to turn it on
  1327.  
  1328. // Also, set the proper MASTER STREAM
  1329.  
  1330.     hr = gcap.pRender->QueryInterface(IID_IConfigAviMux,
  1331.                         (void **)&gcap.pConfigAviMux);
  1332.     if (hr == NOERROR && gcap.pConfigAviMux) {
  1333.     gcap.pConfigAviMux->SetOutputCompatibilityIndex(TRUE);
  1334.     if (gcap.fCapAudio) {
  1335.         hr = gcap.pConfigAviMux->SetMasterStream(gcap.iMasterStream);
  1336.         if (hr != NOERROR)
  1337.         ErrMsg("SetMasterStream failed!");
  1338.     }
  1339.     }
  1340.  
  1341. //
  1342. // Render the video capture and preview pins - even if the capture filter only
  1343. // has a capture pin (and no preview pin) this should work... because the
  1344. // capture graph builder will use a smart tee filter to provide both capture
  1345. // and preview.  We don't have to worry.  It will just work.
  1346. //
  1347.  
  1348. // NOTE that we try to render the interleaved pin before the video pin, because
  1349. // if BOTH exist, it's a DV filter and the only way to get the audio is to use
  1350. // the interleaved pin.  Using the Video pin on a DV filter is only useful if
  1351. // you don't want the audio.
  1352.  
  1353.     hr = gcap.pBuilder->RenderStream(&PIN_CATEGORY_CAPTURE,
  1354.                     &MEDIATYPE_Interleaved,
  1355.                     gcap.pVCap, NULL, gcap.pRender);
  1356.     if (hr != NOERROR) {
  1357.         hr = gcap.pBuilder->RenderStream(&PIN_CATEGORY_CAPTURE,
  1358.                     &MEDIATYPE_Video,
  1359.                     gcap.pVCap, NULL, gcap.pRender);
  1360.         if (hr != NOERROR) {
  1361.         ErrMsg("Cannot render video capture stream");
  1362.         goto SetupCaptureFail;
  1363.     }
  1364.     }
  1365.  
  1366.     if (gcap.fWantPreview) {
  1367.         hr = gcap.pBuilder->RenderStream(&PIN_CATEGORY_PREVIEW,
  1368.                 &MEDIATYPE_Interleaved, gcap.pVCap, NULL, NULL);
  1369.         if (hr == VFW_S_NOPREVIEWPIN) {
  1370.         // preview was faked up for us using the (only) capture pin
  1371.         gcap.fPreviewFaked = TRUE;
  1372.     } else if (hr != S_OK) {
  1373.             hr = gcap.pBuilder->RenderStream(&PIN_CATEGORY_PREVIEW,
  1374.                 &MEDIATYPE_Video, gcap.pVCap, NULL, NULL);
  1375.             if (hr == VFW_S_NOPREVIEWPIN) {
  1376.             // preview was faked up for us using the (only) capture pin
  1377.             gcap.fPreviewFaked = TRUE;
  1378.         } else if (hr != S_OK) {
  1379.             ErrMsg("Cannot render video preview stream");
  1380.             goto SetupCaptureFail;
  1381.         }
  1382.         }
  1383.     }
  1384.  
  1385. //
  1386. // Render the audio capture pin?
  1387. //
  1388.  
  1389.     if (gcap.fCapAudio) {
  1390.     hr = gcap.pBuilder->RenderStream(&PIN_CATEGORY_CAPTURE,
  1391.             &MEDIATYPE_Audio, gcap.pACap, NULL, gcap.pRender);
  1392.     if (hr != NOERROR) {
  1393.         ErrMsg("Cannot render audio capture stream");
  1394.         goto SetupCaptureFail;
  1395.     }
  1396.     }
  1397.  
  1398. //
  1399. // Render the closed captioning pin? It could be a CC or a VBI category pin,
  1400. // depending on the capture driver
  1401. //
  1402.  
  1403.     if (gcap.fCapCC) {
  1404.     hr = gcap.pBuilder->RenderStream(&PIN_CATEGORY_CC, NULL,
  1405.                     gcap.pVCap, NULL, gcap.pRender);
  1406.     if (hr != NOERROR) {
  1407.         hr = gcap.pBuilder->RenderStream(&PIN_CATEGORY_VBI, NULL,
  1408.                     gcap.pVCap, NULL, gcap.pRender);
  1409.         if (hr != NOERROR) {
  1410.             ErrMsg("Cannot render closed captioning");
  1411.             // so what? goto SetupCaptureFail;
  1412.             }
  1413.     }
  1414.      // To preview and capture VBI at the same time, we can call this twice
  1415.         if (gcap.fWantPreview) {
  1416.         hr = gcap.pBuilder->RenderStream(&PIN_CATEGORY_VBI, NULL,
  1417.                     gcap.pVCap, NULL, NULL);
  1418.     }
  1419.     }
  1420.  
  1421. //
  1422. // Get the preview window to be a child of our app's window
  1423. //
  1424.  
  1425.     // This will find the IVideoWindow interface on the renderer.  It is 
  1426.     // important to ask the filtergraph for this interface... do NOT use
  1427.     // ICaptureGraphBuilder2::FindInterface, because the filtergraph needs to
  1428.     // know we own the window so it can give us display changed messages, etc.
  1429.  
  1430.     // NOTE: We do this even if we didn't ask for a preview, because rendering
  1431.     // the capture pin may have rendered the preview pin too (WDM overlay 
  1432.     // devices) because they must have a preview going.  So we better always
  1433.     // put the preview window in our app, or we may get a top level window
  1434.     // appearing out of nowhere!
  1435.  
  1436.        hr = gcap.pFg->QueryInterface(IID_IVideoWindow, (void **)&gcap.pVW);
  1437.         if (hr != NOERROR && gcap.fWantPreview) {
  1438.         ErrMsg("This graph cannot preview");
  1439.         } else if (hr == NOERROR) {
  1440.         RECT rc;
  1441.         gcap.pVW->put_Owner((long)ghwndApp);    // We own the window now
  1442.         gcap.pVW->put_WindowStyle(WS_CHILD);    // you are now a child
  1443.         // give the preview window all our space but where the status bar is
  1444.         GetClientRect(ghwndApp, &rc);
  1445.         cyBorder = GetSystemMetrics(SM_CYBORDER);
  1446.         cy = statusGetHeight() + cyBorder;
  1447.         rc.bottom -= cy;
  1448.         gcap.pVW->SetWindowPosition(0, 0, rc.right, rc.bottom); // be this big
  1449.         gcap.pVW->put_Visible(OATRUE);
  1450.         }
  1451.  
  1452.     // now tell it what frame rate to capture at.  Just find the format it
  1453.     // is capturing with, and leave everything alone but change the frame rate
  1454.     hr = gcap.fUseFrameRate ? E_FAIL : NOERROR;
  1455.     if (gcap.pVSC && gcap.fUseFrameRate) {
  1456.     hr = gcap.pVSC->GetFormat(&pmt);
  1457.     // DV capture does not use a VIDEOINFOHEADER
  1458.         if (hr == NOERROR) {
  1459.         if (pmt->formattype == FORMAT_VideoInfo) {
  1460.             VIDEOINFOHEADER *pvi = (VIDEOINFOHEADER *)pmt->pbFormat;
  1461.             pvi->AvgTimePerFrame = (LONGLONG)(10000000 / gcap.FrameRate);
  1462.             hr = gcap.pVSC->SetFormat(pmt);
  1463.         }
  1464.         DeleteMediaType(pmt);
  1465.         }
  1466.     }
  1467.     if (hr != NOERROR)
  1468.     ErrMsg("Cannot set frame rate for capture");
  1469.  
  1470.     // now ask the filtergraph to tell us when something is completed or aborted
  1471.     // (EC_COMPLETE, EC_USERABORT, EC_ERRORABORT).  This is how we will find out
  1472.     // if the disk gets full while capturing
  1473.     hr = gcap.pFg->QueryInterface(IID_IMediaEventEx, (void **)&gcap.pME);
  1474.     if (hr == NOERROR) {
  1475.     gcap.pME->SetNotifyWindow((LONG)ghwndApp, WM_FGNOTIFY, 0);
  1476.     }
  1477.  
  1478. // All done.
  1479.  
  1480.     // potential debug output - what the graph looks like
  1481.     // DumpGraph(gcap.pFg, 1);
  1482.  
  1483.     gcap.fCaptureGraphBuilt = TRUE;
  1484.     return TRUE;
  1485.  
  1486. SetupCaptureFail:
  1487.     TearDownGraph();
  1488.     return FALSE;
  1489. }
  1490.  
  1491.  
  1492.  
  1493. // build the preview graph!
  1494. //
  1495. // !!! PLEASE NOTE !!!  Some new WDM devices have totally separate capture
  1496. // and preview settings.  An application that wishes to preview and then 
  1497. // capture may have to set the preview pin format using IAMStreamConfig on the
  1498. // preview pin, and then again on the capture pin to capture with that format.
  1499. // In this sample app, there is a separate page to set the settings on the 
  1500. // capture pin and one for the preview pin.  To avoid the user
  1501. // having to enter the same settings in 2 dialog boxes, an app can have its own
  1502. // UI for choosing a format (the possible formats can be enumerated using
  1503. // IAMStreamConfig) and then the app can programmatically call IAMStreamConfig
  1504. // to set the format on both pins.
  1505. //
  1506. BOOL BuildPreviewGraph()
  1507. {
  1508.     int cy, cyBorder;
  1509.     HRESULT hr;
  1510.     AM_MEDIA_TYPE *pmt;
  1511.     BOOL fPreviewUsingCapturePin = FALSE;
  1512.  
  1513.     // we have one already
  1514.     if (gcap.fPreviewGraphBuilt)
  1515.     return TRUE;
  1516.  
  1517.     // No rebuilding while we're running
  1518.     if (gcap.fCapturing || gcap.fPreviewing)
  1519.     return FALSE;
  1520.  
  1521.     // We don't have the necessary capture filters
  1522.     if (gcap.pVCap == NULL)
  1523.     return FALSE;
  1524.     if (gcap.pACap == NULL && gcap.fCapAudio)
  1525.     return FALSE;
  1526.  
  1527.     // we already have another graph built... tear down the old one
  1528.     if (gcap.fCaptureGraphBuilt)
  1529.     TearDownGraph();
  1530.  
  1531. //
  1532. // Render the preview pin - even if there is not preview pin, the capture
  1533. // graph builder will use a smart tee filter and provide a preview.
  1534. //
  1535. // !!! what about latency/buffer issues?
  1536.  
  1537. // NOTE that we try to render the interleaved pin before the video pin, because
  1538. // if BOTH exist, it's a DV filter and the only way to get the audio is to use
  1539. // the interleaved pin.  Using the Video pin on a DV filter is only useful if
  1540. // you don't want the audio.
  1541.  
  1542.     hr = gcap.pBuilder->RenderStream(&PIN_CATEGORY_PREVIEW,
  1543.                 &MEDIATYPE_Interleaved, gcap.pVCap, NULL, NULL);
  1544.     if (hr == VFW_S_NOPREVIEWPIN) {
  1545.     // preview was faked up for us using the (only) capture pin
  1546.     gcap.fPreviewFaked = TRUE;
  1547.     } else if (hr != S_OK) {
  1548.     // maybe it's DV?
  1549.         hr = gcap.pBuilder->RenderStream(&PIN_CATEGORY_PREVIEW,
  1550.                 &MEDIATYPE_Video, gcap.pVCap, NULL, NULL);
  1551.         if (hr == VFW_S_NOPREVIEWPIN) {
  1552.         // preview was faked up for us using the (only) capture pin
  1553.         gcap.fPreviewFaked = TRUE;
  1554.         } else if (hr != S_OK) {
  1555.         ErrMsg("This graph cannot preview!");
  1556.     }
  1557.     }
  1558.  
  1559. //
  1560. // Render the closed captioning pin? It could be a CC or a VBI category pin,
  1561. // depending on the capture driver
  1562. //
  1563.  
  1564.     if (gcap.fCapCC) {
  1565.     hr = gcap.pBuilder->RenderStream(&PIN_CATEGORY_CC, NULL,
  1566.                     gcap.pVCap, NULL, NULL);
  1567.     if (hr != NOERROR) {
  1568.         hr = gcap.pBuilder->RenderStream(&PIN_CATEGORY_VBI, NULL,
  1569.                     gcap.pVCap, NULL, NULL);
  1570.         if (hr != NOERROR) {
  1571.             ErrMsg("Cannot render closed captioning");
  1572.             // so what? goto SetupCaptureFail;
  1573.             }
  1574.         }
  1575.     }
  1576.  
  1577. //
  1578. // Get the preview window to be a child of our app's window
  1579. //
  1580.  
  1581.     // This will find the IVideoWindow interface on the renderer.  It is 
  1582.     // important to ask the filtergraph for this interface... do NOT use
  1583.     // ICaptureGraphBuilder2::FindInterface, because the filtergraph needs to
  1584.     // know we own the window so it can give us display changed messages, etc.
  1585.  
  1586.     hr = gcap.pFg->QueryInterface(IID_IVideoWindow, (void **)&gcap.pVW);
  1587.     if (hr != NOERROR) {
  1588.     ErrMsg("This graph cannot preview properly");
  1589.     } else {
  1590.     RECT rc;
  1591.     gcap.pVW->put_Owner((long)ghwndApp);    // We own the window now
  1592.     gcap.pVW->put_WindowStyle(WS_CHILD);    // you are now a child
  1593.     // give the preview window all our space but where the status bar is
  1594.     GetClientRect(ghwndApp, &rc);
  1595.     cyBorder = GetSystemMetrics(SM_CYBORDER);
  1596.     cy = statusGetHeight() + cyBorder;
  1597.     rc.bottom -= cy;
  1598.     gcap.pVW->SetWindowPosition(0, 0, rc.right, rc.bottom); // be this big
  1599.     gcap.pVW->put_Visible(OATRUE);
  1600.     }
  1601.  
  1602.     // now tell it what frame rate to capture at.  Just find the format it
  1603.     // is capturing with, and leave everything alone but change the frame rate
  1604.     // No big deal if it fails.  It's just for preview
  1605.     // !!! Should we then talk to the preview pin?
  1606.     if (gcap.pVSC && gcap.fUseFrameRate) {
  1607.     hr = gcap.pVSC->GetFormat(&pmt);
  1608.     // DV capture does not use a VIDEOINFOHEADER
  1609.         if (hr == NOERROR) {
  1610.         if (pmt->formattype == FORMAT_VideoInfo) {
  1611.             VIDEOINFOHEADER *pvi = (VIDEOINFOHEADER *)pmt->pbFormat;
  1612.             pvi->AvgTimePerFrame = (LONGLONG)(10000000 / gcap.FrameRate);
  1613.             hr = gcap.pVSC->SetFormat(pmt);
  1614.         if (hr != NOERROR)
  1615.             ErrMsg("%x: Cannot set frame rate for preview", hr);
  1616.         }
  1617.         DeleteMediaType(pmt);
  1618.     }
  1619.     }
  1620.  
  1621.     // make sure we process events while we're previewing!
  1622.     hr = gcap.pFg->QueryInterface(IID_IMediaEventEx, (void **)&gcap.pME);
  1623.     if (hr == NOERROR) {
  1624.         gcap.pME->SetNotifyWindow((LONG)ghwndApp, WM_FGNOTIFY, 0);
  1625.     }
  1626.  
  1627.  
  1628. // All done.
  1629.  
  1630.     // potential debug output - what the graph looks like
  1631.     // DumpGraph(gcap.pFg, 1);
  1632.  
  1633.     gcap.fPreviewGraphBuilt = TRUE;
  1634.     return TRUE;
  1635. }
  1636.  
  1637.  
  1638. // Start previewing
  1639. //
  1640. BOOL StartPreview()
  1641. {
  1642.     BOOL f = TRUE;
  1643.  
  1644.     // way ahead of you
  1645.     if (gcap.fPreviewing)
  1646.     return TRUE;
  1647.  
  1648.     if (!gcap.fPreviewGraphBuilt)
  1649.     return FALSE;
  1650.  
  1651.     // run the graph
  1652.     IMediaControl *pMC = NULL;
  1653.     HRESULT hr = gcap.pFg->QueryInterface(IID_IMediaControl, (void **)&pMC);
  1654.     if (SUCCEEDED(hr)) {
  1655.     hr = pMC->Run();
  1656.     if (FAILED(hr)) {
  1657.         // stop parts that ran
  1658.         pMC->Stop();
  1659.     }
  1660.     pMC->Release();
  1661.     }
  1662.     if (FAILED(hr)) {
  1663.     ErrMsg("Error %x: Cannot run preview graph", hr);
  1664.     return FALSE;
  1665.     }
  1666.  
  1667.     gcap.fPreviewing = TRUE;
  1668.     return TRUE;
  1669. }
  1670.  
  1671.  
  1672. // stop the preview graph
  1673. //
  1674. BOOL StopPreview()
  1675. {
  1676.     // way ahead of you
  1677.     if (!gcap.fPreviewing) {
  1678.     return FALSE;
  1679.     }
  1680.  
  1681.     // stop the graph
  1682.     IMediaControl *pMC = NULL;
  1683.     HRESULT hr = gcap.pFg->QueryInterface(IID_IMediaControl, (void **)&pMC);
  1684.     if (SUCCEEDED(hr)) {
  1685.     hr = pMC->Stop();
  1686.     pMC->Release();
  1687.     }
  1688.     if (FAILED(hr)) {
  1689.     ErrMsg("Error %x: Cannot stop preview graph", hr);
  1690.     return FALSE;
  1691.     }
  1692.  
  1693.     gcap.fPreviewing = FALSE;
  1694.  
  1695.     // !!! get rid of menu garbage
  1696.     InvalidateRect(ghwndApp, NULL, TRUE);
  1697.  
  1698.     return TRUE;
  1699. }
  1700.  
  1701.  
  1702. // start the capture graph
  1703. //
  1704. BOOL StartCapture()
  1705. {
  1706.     BOOL f, fHasStreamControl;
  1707.     HRESULT hr;
  1708.  
  1709.     // way ahead of you
  1710.     if (gcap.fCapturing)
  1711.     return TRUE;
  1712.  
  1713.     // or we'll get confused
  1714.     if (gcap.fPreviewing)
  1715.     StopPreview();
  1716.  
  1717.     // or we'll crash
  1718.     if (!gcap.fCaptureGraphBuilt)
  1719.     return FALSE;
  1720.  
  1721.     // This amount will be subtracted from the number of dropped and not 
  1722.     // dropped frames reported by the filter.  Since we might be having the
  1723.     // filter running while the pin is turned off, we don't want any of the
  1724.     // frame statistics from the time the pin is off interfering with the
  1725.     // statistics we gather while the pin is on
  1726.     gcap.lDroppedBase = 0;
  1727.     gcap.lNotBase = 0;
  1728.  
  1729.     REFERENCE_TIME start = MAX_TIME, stop = MAX_TIME;
  1730.  
  1731.     // don't capture quite yet...
  1732.     hr = gcap.pBuilder->ControlStream(&PIN_CATEGORY_CAPTURE, NULL,
  1733.                 NULL, &start, NULL, 0, 0);
  1734.     //DbgLog((LOG_TRACE,1,TEXT("Capture OFF returns %x"), hr));
  1735.  
  1736.     // Do we have the ability to control capture and preview separately?
  1737.     fHasStreamControl = SUCCEEDED(hr);
  1738.  
  1739.     // prepare to run the graph
  1740.     IMediaControl *pMC = NULL;
  1741.     hr = gcap.pFg->QueryInterface(IID_IMediaControl, (void **)&pMC);
  1742.     if (FAILED(hr)) {
  1743.     ErrMsg("Error %x: Cannot get IMediaControl", hr);
  1744.     return FALSE;
  1745.     }
  1746.  
  1747.     // If we were able to keep capture off, then we can
  1748.     // run the graph now for frame accurate start later yet still showing a
  1749.     // preview.   Otherwise, we can't run the graph yet without capture
  1750.     // starting too, so we'll pause it so the latency between when they
  1751.     // press a key and when capture begins is still small (but they won't have
  1752.     // a preview while they wait to press a key)
  1753.  
  1754.     if (fHasStreamControl)
  1755.     hr = pMC->Run();
  1756.     else
  1757.     hr = pMC->Pause();
  1758.     if (FAILED(hr)) {
  1759.     // stop parts that started
  1760.     pMC->Stop();
  1761.     pMC->Release();
  1762.     ErrMsg("Error %x: Cannot start graph", hr);
  1763.     return FALSE;
  1764.     }
  1765.  
  1766.     // press a key to start capture
  1767.     f = DoDialog(ghwndApp, IDD_PressAKeyDialog, (DLGPROC)PressAKeyProc, 0);
  1768.     if (!f) {
  1769.     pMC->Stop();
  1770.     pMC->Release();
  1771.     if (gcap.fWantPreview) {
  1772.         BuildPreviewGraph();
  1773.         StartPreview();
  1774.     }
  1775.     return f;
  1776.     }
  1777.  
  1778.     // Start capture NOW!
  1779.     if (fHasStreamControl) {
  1780.     // we may not have this yet
  1781.         if (!gcap.pDF) {
  1782.         hr = gcap.pBuilder->FindInterface(&PIN_CATEGORY_CAPTURE,
  1783.                    &MEDIATYPE_Interleaved, gcap.pVCap,
  1784.                    IID_IAMDroppedFrames, (void **)&gcap.pDF);
  1785.         if (hr != NOERROR)
  1786.             hr = gcap.pBuilder->FindInterface(&PIN_CATEGORY_CAPTURE,
  1787.                    &MEDIATYPE_Video, gcap.pVCap,
  1788.                    IID_IAMDroppedFrames, (void **)&gcap.pDF);
  1789.     }
  1790.  
  1791.     // turn the capture pin on now!
  1792.     hr = gcap.pBuilder->ControlStream(&PIN_CATEGORY_CAPTURE, NULL,
  1793.                 NULL, NULL, &stop, 0, 0);
  1794.         //DbgLog((LOG_TRACE,0,TEXT("Capture ON returns %x"), hr));
  1795.     // make note of the current dropped frame counts
  1796.     if (gcap.pDF) {
  1797.         gcap.pDF->GetNumDropped(&gcap.lDroppedBase);
  1798.         gcap.pDF->GetNumNotDropped(&gcap.lNotBase);
  1799.             //DbgLog((LOG_TRACE,0,TEXT("Dropped counts are %ld and %ld"),
  1800.         //        gcap.lDroppedBase, gcap.lNotBase));
  1801.         } 
  1802.     } else {
  1803.     hr = pMC->Run();
  1804.     if (FAILED(hr)) {
  1805.         // stop parts that started
  1806.         pMC->Stop();
  1807.         pMC->Release();
  1808.         ErrMsg("Error %x: Cannot run graph", hr);
  1809.         return FALSE;
  1810.     }
  1811.     }
  1812.  
  1813.     pMC->Release();
  1814.  
  1815.     // when did we start capture?
  1816.     gcap.lCapStartTime = timeGetTime();
  1817.  
  1818.     // 30 times a second I want to update my status bar - #captured, #dropped
  1819.     SetTimer(ghwndApp, 1, 33, NULL);
  1820.  
  1821.     gcap.fCapturing = TRUE;
  1822.     return TRUE;
  1823. }
  1824.  
  1825.  
  1826. // stop the capture graph
  1827. //
  1828. BOOL StopCapture()
  1829. {
  1830.     // way ahead of you
  1831.     if (!gcap.fCapturing) {
  1832.     return FALSE;
  1833.     }
  1834.  
  1835.     // stop the graph
  1836.     IMediaControl *pMC = NULL;
  1837.     HRESULT hr = gcap.pFg->QueryInterface(IID_IMediaControl, (void **)&pMC);
  1838.     if (SUCCEEDED(hr)) {
  1839.     hr = pMC->Stop();
  1840.     pMC->Release();
  1841.     }
  1842.     if (FAILED(hr)) {
  1843.     ErrMsg("Error %x: Cannot stop graph", hr);
  1844.     return FALSE;
  1845.     }
  1846.  
  1847.     // when the graph was stopped
  1848.     gcap.lCapStopTime = timeGetTime();
  1849.  
  1850.     // no more status bar updates
  1851.     KillTimer(ghwndApp, 1);
  1852.  
  1853.     // one last time for the final count and all the stats
  1854.     UpdateStatus(TRUE);
  1855.  
  1856.     gcap.fCapturing = FALSE;
  1857.  
  1858.     // !!! get rid of menu garbage
  1859.     InvalidateRect(ghwndApp, NULL, TRUE);
  1860.  
  1861.     return TRUE;
  1862. }
  1863.  
  1864.  
  1865. // Let's talk about UI for a minute.  There are many programmatic interfaces
  1866. // you can use to program a capture filter or related filter to capture the
  1867. // way you want it to.... eg:  IAMStreamConfig, IAMVideoCompression,
  1868. // IAMCrossbar, IAMTVTuner, IAMTVAudio, IAMAnalogVideoDecoder, IAMCameraControl,
  1869. // IAMVideoProcAmp, etc.
  1870. //
  1871. // But you probably want some UI to let the user play with all these settings.
  1872. // For new WDM-style capture devices, we offer some default UI you can use.
  1873. // The code below shows how to bring up all of the dialog boxes supported 
  1874. // by any capture filters.
  1875. //
  1876. // The following code shows you how you can bring up all of the
  1877. // dialogs supported by a particular object at once on a big page with lots
  1878. // of thumb tabs.  You do this by starting with an interface on the object that
  1879. // you want, and using ISpecifyPropertyPages to get the whole list, and
  1880. // OleCreatePropertyFrame to bring them all up.  This way you will get custom
  1881. // property pages a filter has, too, that are not one of the standard pages that
  1882. // you know about.  There are at least 9 objects that may have property pages.
  1883. // Your app already has 2 of the object pointers, the video capture filter and
  1884. // the audio capture filter (let's call them pVCap and pACap)
  1885. // 1.  The video capture filter - pVCap
  1886. // 2.  The video capture filter's capture pin - get this by calling
  1887. //     FindInterface(&PIN_CATEGORY_CAPTURE, pVCap, IID_IPin, &pX);
  1888. // 3.  The video capture filter's preview pin - get this by calling
  1889. //     FindInterface(&PIN_CATEGORY_PREVIEW, pVCap, IID_IPin, &pX);
  1890. // 4.  The audio capture filter - pACap
  1891. // 5.  The audio capture filter's capture pin - get this by calling
  1892. //     FindInterface(&PIN_CATEGORY_CAPTURE, pACap, IID_IPin, &pX);
  1893. // 6.  The crossbar connected to the video capture filter - get this by calling
  1894. //     FindInterface(NULL, pVCap, IID_IAMCrossbar, &pX);
  1895. // 7.  There is a possible second crossbar to control audio - get this by 
  1896. //     looking upstream of the first crossbar like this:
  1897. //     FindInterface(&LOOK_UPSTREAM_ONLY, pX, IID_IAMCrossbar, &pX2);
  1898. // 8.  The TV Tuner connected to the video capture filter - get this by calling
  1899. //     FindInterface(NULL, pVCap, IID_IAMTVTuner, &pX);
  1900. // 9.  The TV Audio connected to the audio capture filter - get this by calling
  1901. //     FindInterface(NULL, pACap, IID_IAMTVAudio, &pX);
  1902. // 10. We have a helper class, CCrossbar, which makes the crossbar issue less
  1903. //     confusing.  In fact, although not supported here, there may be more than
  1904. //     two crossbars, arranged in many different ways.  An application may not
  1905. //     wish to have separate dialogs for each crossbar, but instead hide the
  1906. //     complexity and simply offer the user a list of inputs that can be chosen.
  1907. //     This list represents all the unique inputs from all the crossbars.
  1908. //     The crossbar helper class does this and offers that list as #10.  It is
  1909. //     expected that an application will either provide the crossbar dialogs
  1910. //     above (#6 and #7) OR provide the input list (this #10), but not both.
  1911. //     That would be confusing because if you select an input using dialog 6 or
  1912. //     7 the input list here in #10 won't know about your choice.
  1913. //
  1914. // Your last choice for UI is to make your own pages, and use the results of 
  1915. // your custom page to call the interfaces programmatically.
  1916.  
  1917.  
  1918. void MakeMenuOptions()
  1919. {
  1920.     HRESULT hr;
  1921.     HMENU hMenuSub = GetSubMenu(GetMenu(ghwndApp), 2); // Options menu
  1922.  
  1923.     // remove any old choices from the last device
  1924.     RemoveMenu(hMenuSub, 4, MF_BYPOSITION);
  1925.     RemoveMenu(hMenuSub, 4, MF_BYPOSITION);
  1926.     RemoveMenu(hMenuSub, 4, MF_BYPOSITION);
  1927.     RemoveMenu(hMenuSub, 4, MF_BYPOSITION);
  1928.     RemoveMenu(hMenuSub, 4, MF_BYPOSITION);
  1929.     RemoveMenu(hMenuSub, 4, MF_BYPOSITION);
  1930.     RemoveMenu(hMenuSub, 4, MF_BYPOSITION);
  1931.     RemoveMenu(hMenuSub, 4, MF_BYPOSITION);
  1932.     RemoveMenu(hMenuSub, 4, MF_BYPOSITION);
  1933.     RemoveMenu(hMenuSub, 4, MF_BYPOSITION);
  1934.  
  1935.     int zz = 0;
  1936.     gcap.iFormatDialogPos = -1;
  1937.     gcap.iSourceDialogPos = -1;
  1938.     gcap.iDisplayDialogPos = -1;
  1939.     gcap.iVCapDialogPos = -1;
  1940.     gcap.iVCrossbarDialogPos = -1;
  1941.     gcap.iTVTunerDialogPos = -1;
  1942.     gcap.iACapDialogPos = -1;
  1943.     gcap.iACrossbarDialogPos = -1;
  1944.     gcap.iTVAudioDialogPos = -1;
  1945.     gcap.iVCapCapturePinDialogPos = -1;
  1946.     gcap.iVCapPreviewPinDialogPos = -1;
  1947.     gcap.iACapCapturePinDialogPos = -1;
  1948.  
  1949.     // If this device supports the old legacy UI dialogs, offer them
  1950.  
  1951.     if (gcap.pDlg && !gcap.pDlg->HasDialog(VfwCaptureDialog_Format)) {
  1952.     AppendMenu(hMenuSub, MF_STRING, MENU_DIALOG0 + zz, TEXT("Video Format..."));
  1953.     gcap.iFormatDialogPos = zz++;
  1954.     }
  1955.     if (gcap.pDlg && !gcap.pDlg->HasDialog(VfwCaptureDialog_Source)) {
  1956.     AppendMenu(hMenuSub, MF_STRING, MENU_DIALOG0 + zz, TEXT("Video Source..."));
  1957.     gcap.iSourceDialogPos = zz++;
  1958.     }
  1959.     if (gcap.pDlg && !gcap.pDlg->HasDialog(VfwCaptureDialog_Display)) {
  1960.     AppendMenu(hMenuSub, MF_STRING, MENU_DIALOG0 + zz, TEXT("Video Display..."));
  1961.     gcap.iDisplayDialogPos = zz++;
  1962.     }
  1963.  
  1964.     // Also check the audio capture filter at this point, since even non wdm devices
  1965.     // may support an IAMAudioInputMixer property page (we'll also get any wdm filter
  1966.     // properties here as well). We'll get any audio capture pin property pages just
  1967.     // a bit later.
  1968.     if (gcap.pACap != NULL)
  1969.     {
  1970.         ISpecifyPropertyPages *pSpec;
  1971.         CAUUID cauuid;
  1972.         
  1973.         hr = gcap.pACap->QueryInterface(IID_ISpecifyPropertyPages, (void **)&pSpec);
  1974.         if (hr == S_OK) {
  1975.             hr = pSpec->GetPages(&cauuid);
  1976.             if (hr == S_OK && cauuid.cElems > 0) {
  1977.                 AppendMenu(hMenuSub,MF_STRING,MENU_DIALOG0+zz, TEXT("Audio Capture Filter..."));
  1978.                 gcap.iACapDialogPos = zz++;
  1979.                 CoTaskMemFree(cauuid.pElems);
  1980.             }
  1981.             pSpec->Release();
  1982.         }
  1983.     }
  1984.  
  1985.     // don't bother looking for new property pages if the old ones are supported
  1986.     // or if we don't have a capture filter
  1987.     if (gcap.pVCap == NULL || gcap.iFormatDialogPos != -1)
  1988.     return;
  1989.  
  1990.     // New WDM devices support new UI and new interfaces.
  1991.     // Your app can use some default property
  1992.     // pages for UI if you'd like (like we do here) or if you don't like our
  1993.     // dialog boxes, feel free to make your own and programmatically set 
  1994.     // the capture options through interfaces like IAMCrossbar, IAMCameraControl
  1995.     // etc.
  1996.  
  1997.     // There are 9 objects that might support property pages.  Let's go through
  1998.     // them.
  1999.  
  2000.     ISpecifyPropertyPages *pSpec;
  2001.     CAUUID cauuid;
  2002.  
  2003.     // 1. the video capture filter itself
  2004.  
  2005.     hr = gcap.pVCap->QueryInterface(IID_ISpecifyPropertyPages, (void **)&pSpec);
  2006.     if (hr == S_OK) {
  2007.         hr = pSpec->GetPages(&cauuid);
  2008.         if (hr == S_OK && cauuid.cElems > 0) {
  2009.         AppendMenu(hMenuSub,MF_STRING,MENU_DIALOG0+zz, TEXT("Video Capture Filter..."));
  2010.         gcap.iVCapDialogPos = zz++;
  2011.         CoTaskMemFree(cauuid.pElems);
  2012.     }
  2013.     pSpec->Release();
  2014.     }
  2015.  
  2016.     // 2.  The video capture capture pin
  2017.  
  2018.     IAMStreamConfig *pSC;
  2019.     hr = gcap.pBuilder->FindInterface(&PIN_CATEGORY_CAPTURE,
  2020.             &MEDIATYPE_Interleaved,
  2021.             gcap.pVCap, IID_IAMStreamConfig, (void **)&pSC);
  2022.     if (hr != S_OK)
  2023.         hr = gcap.pBuilder->FindInterface(&PIN_CATEGORY_CAPTURE,
  2024.                 &MEDIATYPE_Video, gcap.pVCap,
  2025.                 IID_IAMStreamConfig, (void **)&pSC);
  2026.     if (hr == S_OK) {
  2027.         hr = pSC->QueryInterface(IID_ISpecifyPropertyPages, (void **)&pSpec);
  2028.         if (hr == S_OK) {
  2029.             hr = pSpec->GetPages(&cauuid);
  2030.             if (hr == S_OK && cauuid.cElems > 0) {
  2031.             AppendMenu(hMenuSub,MF_STRING,MENU_DIALOG0+zz, TEXT("Video Capture Pin..."));
  2032.             gcap.iVCapCapturePinDialogPos = zz++;
  2033.             CoTaskMemFree(cauuid.pElems);
  2034.         }
  2035.         pSpec->Release();
  2036.         }
  2037.     pSC->Release();
  2038.     }
  2039.  
  2040.     // 3.  The video capture preview pin.
  2041.     // This basically sets the format being previewed.  Typically, you
  2042.     // want to capture and preview using the SAME format, instead of having to
  2043.     // enter the same value in 2 dialog boxes.  For a discussion on this, see
  2044.     // the comment above the MakePreviewGraph function.
  2045.  
  2046.     hr = gcap.pBuilder->FindInterface(&PIN_CATEGORY_PREVIEW,
  2047.                 &MEDIATYPE_Interleaved, gcap.pVCap,
  2048.                 IID_IAMStreamConfig, (void **)&pSC);
  2049.     if (hr != NOERROR)
  2050.         hr = gcap.pBuilder->FindInterface(&PIN_CATEGORY_PREVIEW,
  2051.                 &MEDIATYPE_Video, gcap.pVCap,
  2052.                 IID_IAMStreamConfig, (void **)&pSC);
  2053.     if (hr == S_OK) {
  2054.         hr = pSC->QueryInterface(IID_ISpecifyPropertyPages, (void **)&pSpec);
  2055.         if (hr == S_OK) {
  2056.             hr = pSpec->GetPages(&cauuid);
  2057.             if (hr == S_OK && cauuid.cElems > 0) {
  2058.             AppendMenu(hMenuSub,MF_STRING,MENU_DIALOG0+zz,TEXT("Video Preview Pin..."));
  2059.             gcap.iVCapPreviewPinDialogPos = zz++;
  2060.         CoTaskMemFree(cauuid.pElems);
  2061.         }
  2062.         pSpec->Release();
  2063.         }
  2064.     pSC->Release();
  2065.     }
  2066.  
  2067.     // 4 & 5.  The video crossbar, and a possible second crossbar
  2068.  
  2069.     IAMCrossbar *pX, *pX2;
  2070.     IBaseFilter *pXF;
  2071.     hr = gcap.pBuilder->FindInterface(&PIN_CATEGORY_CAPTURE,
  2072.                 &MEDIATYPE_Interleaved, gcap.pVCap,
  2073.                 IID_IAMCrossbar, (void **)&pX);
  2074.     if (hr != S_OK)
  2075.         hr = gcap.pBuilder->FindInterface(&PIN_CATEGORY_CAPTURE,
  2076.                 &MEDIATYPE_Video, gcap.pVCap,
  2077.                 IID_IAMCrossbar, (void **)&pX);
  2078.     if (hr == S_OK) {
  2079.         hr = pX->QueryInterface(IID_IBaseFilter, (void **)&pXF);
  2080.         if (hr == S_OK) {
  2081.             hr = pX->QueryInterface(IID_ISpecifyPropertyPages, (void **)&pSpec);
  2082.             if (hr == S_OK) {
  2083.                 hr = pSpec->GetPages(&cauuid);
  2084.                 if (hr == S_OK && cauuid.cElems > 0) {
  2085.                 AppendMenu(hMenuSub,MF_STRING,MENU_DIALOG0+zz,
  2086.                         TEXT("Video Crossbar..."));
  2087.                 gcap.iVCrossbarDialogPos = zz++;
  2088.             CoTaskMemFree(cauuid.pElems);
  2089.             }
  2090.             pSpec->Release();
  2091.             }
  2092.             hr = gcap.pBuilder->FindInterface(&LOOK_UPSTREAM_ONLY, NULL, pXF,
  2093.                 IID_IAMCrossbar, (void **)&pX2);
  2094.             if (hr == S_OK) {
  2095.                 hr = pX2->QueryInterface(IID_ISpecifyPropertyPages,
  2096.                             (void **)&pSpec);
  2097.                 if (hr == S_OK) {
  2098.                     hr = pSpec->GetPages(&cauuid);
  2099.                     if (hr == S_OK && cauuid.cElems > 0) {
  2100.                     AppendMenu(hMenuSub,MF_STRING,MENU_DIALOG0+zz,
  2101.                         TEXT("Second Crossbar..."));
  2102.                     gcap.iACrossbarDialogPos = zz++;
  2103.                 CoTaskMemFree(cauuid.pElems);
  2104.                 }
  2105.                 pSpec->Release();
  2106.                 }
  2107.             pX2->Release();
  2108.         }
  2109.          pXF->Release();
  2110.         }
  2111.     pX->Release();
  2112.     }
  2113.  
  2114.     // 6.  The TVTuner
  2115.  
  2116.     IAMTVTuner *pTV;
  2117.     hr = gcap.pBuilder->FindInterface(&PIN_CATEGORY_CAPTURE,
  2118.                 &MEDIATYPE_Interleaved, gcap.pVCap,
  2119.                 IID_IAMTVTuner, (void **)&pTV);
  2120.     if (hr != S_OK)
  2121.         hr = gcap.pBuilder->FindInterface(&PIN_CATEGORY_CAPTURE,
  2122.                 &MEDIATYPE_Video, gcap.pVCap,
  2123.                 IID_IAMTVTuner, (void **)&pTV);
  2124.     if (hr == S_OK) {
  2125.         hr = pTV->QueryInterface(IID_ISpecifyPropertyPages, (void **)&pSpec);
  2126.         if (hr == S_OK) {
  2127.             hr = pSpec->GetPages(&cauuid);
  2128.             if (hr == S_OK && cauuid.cElems > 0) {
  2129.             AppendMenu(hMenuSub,MF_STRING,MENU_DIALOG0+zz, TEXT("TV Tuner..."));
  2130.             gcap.iTVTunerDialogPos = zz++;
  2131.         CoTaskMemFree(cauuid.pElems);
  2132.         }
  2133.         pSpec->Release();
  2134.         }
  2135.      pTV->Release();
  2136.     }
  2137.  
  2138.     // no audio capture, we're done
  2139.     if (gcap.pACap == NULL)
  2140.     return;
  2141.  
  2142.     // 7.  The Audio capture filter itself... Thanks anyway, but we got these already
  2143.  
  2144.     // 8.  The Audio capture pin
  2145.  
  2146.     hr = gcap.pBuilder->FindInterface(&PIN_CATEGORY_CAPTURE,
  2147.                 &MEDIATYPE_Audio, gcap.pACap,
  2148.                 IID_IAMStreamConfig, (void **)&pSC);
  2149.     if (hr == S_OK) {
  2150.         hr = pSC->QueryInterface(IID_ISpecifyPropertyPages, (void **)&pSpec);
  2151.         if (hr == S_OK) {
  2152.             hr = pSpec->GetPages(&cauuid);
  2153.             if (hr == S_OK && cauuid.cElems > 0) {
  2154.             AppendMenu(hMenuSub,MF_STRING,MENU_DIALOG0+zz, TEXT("Audio Capture Pin..."));
  2155.             gcap.iACapCapturePinDialogPos = zz++;
  2156.         CoTaskMemFree(cauuid.pElems);
  2157.         }
  2158.         pSpec->Release();
  2159.         }
  2160.      pSC->Release();
  2161.     }
  2162.  
  2163.     // 9.  The TV Audio filter
  2164.  
  2165.     IAMTVAudio *pTVA;
  2166.     hr = gcap.pBuilder->FindInterface(&PIN_CATEGORY_CAPTURE, 
  2167.                 &MEDIATYPE_Audio, gcap.pACap,
  2168.                 IID_IAMTVAudio, (void **)&pTVA);
  2169.     if (hr == S_OK) {
  2170.         hr = pTVA->QueryInterface(IID_ISpecifyPropertyPages, (void **)&pSpec);
  2171.         if (hr == S_OK) {
  2172.             hr = pSpec->GetPages(&cauuid);
  2173.             if (hr == S_OK && cauuid.cElems > 0) {
  2174.             AppendMenu(hMenuSub,MF_STRING,MENU_DIALOG0+zz, TEXT("TV Audio..."));
  2175.             gcap.iTVAudioDialogPos = zz++;
  2176.         CoTaskMemFree(cauuid.pElems);
  2177.         }
  2178.         pSpec->Release();
  2179.         }
  2180.      pTVA->Release();
  2181.     }
  2182.  
  2183.     // 10.  Crossbar class helper menu item, to let you choose an input
  2184.  
  2185.     if (gcap.pCrossbar && gcap.NumberOfVideoInputs) {
  2186.         gcap.hMenuPopup = CreatePopupMenu();
  2187.         LONG j;
  2188.         LONG  PhysicalType;
  2189.         TCHAR buf[MAX_PATH];
  2190.         LONG InputToEnable = -1;
  2191.  
  2192.     gcap.iVideoInputMenuPos = zz++;
  2193.         AppendMenu(hMenuSub, MF_SEPARATOR, 0, NULL);
  2194.  
  2195.         for (j = 0; j < gcap.NumberOfVideoInputs; j++) {
  2196.             EXECUTE_ASSERT (S_OK == gcap.pCrossbar->GetInputType (j, &PhysicalType));
  2197.             EXECUTE_ASSERT (S_OK == gcap.pCrossbar->GetInputName (j, buf, sizeof (buf)));
  2198.             AppendMenu(gcap.hMenuPopup,MF_STRING,MENU_DIALOG0+zz, buf);
  2199.             zz++;
  2200.  
  2201.             // Route the first TVTuner by default
  2202.             if ((PhysicalType == PhysConn_Video_Tuner) && InputToEnable == -1) {
  2203.                 InputToEnable = j;
  2204.             }
  2205.         }
  2206.             
  2207.         AppendMenu(hMenuSub, MF_STRING | MF_POPUP, (UINT)gcap.hMenuPopup, TEXT("Video Input"));
  2208.  
  2209.         if (InputToEnable == -1) {
  2210.             InputToEnable = 0;
  2211.         }
  2212.         CheckMenuItem(gcap.hMenuPopup, InputToEnable, MF_BYPOSITION | MF_CHECKED); 
  2213.  
  2214.         gcap.pCrossbar->SetInputIndex (InputToEnable);
  2215.     }
  2216.     // !!! anything needed to delete the popup when selecting a new input?
  2217. }
  2218.  
  2219.  
  2220. // how many captured/dropped so far
  2221. //
  2222. void UpdateStatus(BOOL fAllStats)
  2223. {
  2224.     HRESULT hr;
  2225.     LONG lDropped, lNot, lAvgFrameSize;
  2226.     TCHAR tach[160];
  2227.  
  2228.     // we use this interface to get the number of captured and dropped frames
  2229.     // NOTE:  We cannot query for this interface earlier, as it may not be
  2230.     // available until the pin is connected
  2231.     if (!gcap.pDF) {
  2232.     hr = gcap.pBuilder->FindInterface(&PIN_CATEGORY_CAPTURE,
  2233.                 &MEDIATYPE_Interleaved, gcap.pVCap,
  2234.                 IID_IAMDroppedFrames, (void **)&gcap.pDF);
  2235.     if (hr != S_OK)
  2236.         hr = gcap.pBuilder->FindInterface(&PIN_CATEGORY_CAPTURE,
  2237.                 &MEDIATYPE_Video, gcap.pVCap,
  2238.                 IID_IAMDroppedFrames, (void **)&gcap.pDF);
  2239.     }
  2240.  
  2241.     // this filter can't tell us dropped frame info.
  2242.     if (!gcap.pDF) {
  2243.     statusUpdateStatus(ghwndStatus,
  2244.             TEXT("Filter cannot report capture information"));
  2245.     return;
  2246.     }
  2247.  
  2248.     hr = gcap.pDF->GetNumDropped(&lDropped);
  2249.     if (hr == S_OK)
  2250.     hr = gcap.pDF->GetNumNotDropped(&lNot);
  2251.     if (hr != S_OK)
  2252.     return;
  2253.  
  2254.     lDropped -= gcap.lDroppedBase;
  2255.     lNot -= gcap.lNotBase;
  2256.  
  2257.     if (!fAllStats) {
  2258.     LONG lTime = timeGetTime() - gcap.lCapStartTime;
  2259.     wsprintf(tach, TEXT("Captured %d frames (%d dropped) %d.%dsec"), lNot,
  2260.                 lDropped, lTime / 1000, 
  2261.                 lTime / 100 - lTime / 1000 * 10);
  2262.     statusUpdateStatus(ghwndStatus, tach);
  2263.     return;
  2264.     }
  2265.  
  2266.     // we want all possible stats, including capture time and actual acheived
  2267.     // frame rate and data rate (as opposed to what we tried to get).  These
  2268.     // numbers are an indication that though we dropped frames just now, if we
  2269.     // chose a data rate and frame rate equal to the numbers I'm about to
  2270.     // print, we probably wouldn't drop any frames.
  2271.     
  2272.     // average size of frame captured
  2273.     hr = gcap.pDF->GetAverageFrameSize(&lAvgFrameSize);
  2274.     if (hr != S_OK)
  2275.     return;
  2276.  
  2277.     // how long capture lasted
  2278.     LONG lDurMS = gcap.lCapStopTime - gcap.lCapStartTime;
  2279.     double flFrame;     // acheived frame rate
  2280.     LONG lData;         // acheived data rate
  2281.  
  2282.     if (lDurMS > 0) {
  2283.     flFrame = (double)(LONGLONG)lNot * 1000. /
  2284.                         (double)(LONGLONG)lDurMS;
  2285.     lData = (LONG)(LONGLONG)(lNot / (double)(LONGLONG)lDurMS *
  2286.                 1000. * (double)(LONGLONG)lAvgFrameSize);
  2287.     } else {
  2288.     flFrame = 0.;
  2289.     lData = 0;
  2290.     }
  2291.  
  2292.     wsprintf(tach, TEXT("Captured %d frames in %d.%d sec (%d dropped): %d.%d fps %d.%d Meg/sec"),
  2293.         lNot, lDurMS / 1000, lDurMS / 100 - lDurMS / 1000 * 10,
  2294.         lDropped, (int)flFrame,
  2295.         (int)(flFrame * 10.) - (int)flFrame * 10,
  2296.         lData / 1000000,
  2297.         lData / 1000 - (lData / 1000000 * 1000));
  2298.     statusUpdateStatus(ghwndStatus, tach);
  2299. }
  2300.  
  2301.  
  2302. // Check the devices we're currently using and make filters for them
  2303. //
  2304. void ChooseDevices(IMoniker *pmVideo, IMoniker *pmAudio)
  2305. {
  2306.     USES_CONVERSION;
  2307.     #define VERSIZE 40
  2308.     #define DESCSIZE 80
  2309.     int versize = VERSIZE;
  2310.     int descsize = DESCSIZE;
  2311.     WCHAR wachVer[VERSIZE], wachDesc[DESCSIZE];
  2312.     TCHAR tachStatus[VERSIZE + DESCSIZE + 5];
  2313.  
  2314.  
  2315.       // they chose a new device. rebuild the graphs
  2316.     if (gcap.pmVideo != pmVideo || gcap.pmAudio != pmAudio)
  2317.     {
  2318.         if(pmVideo) {
  2319.             pmVideo->AddRef();
  2320.         }
  2321.         if(pmAudio) {
  2322.             pmAudio->AddRef();
  2323.         }
  2324.         IMonRelease(gcap.pmVideo);
  2325.         IMonRelease(gcap.pmAudio);
  2326.         gcap.pmVideo = pmVideo;
  2327.         gcap.pmAudio = pmAudio;
  2328.         if (gcap.fPreviewing)
  2329.             StopPreview();
  2330.         if (gcap.fCaptureGraphBuilt || gcap.fPreviewGraphBuilt)
  2331.             TearDownGraph();
  2332.         FreeCapFilters();
  2333.         InitCapFilters();
  2334.         if (gcap.fWantPreview) { // were we previewing?
  2335.             BuildPreviewGraph();
  2336.             StartPreview();
  2337.         }
  2338.         MakeMenuOptions();    // the UI choices change per device
  2339.     }
  2340.  
  2341.     // Set the check marks for the devices menu.
  2342.     int i;
  2343.     for(i = 0; i < NUMELMS(gcap.rgpmVideoMenu); i++) {
  2344.         if (gcap.rgpmVideoMenu[i] == NULL)
  2345.             break;
  2346.         CheckMenuItem(  GetMenu(ghwndApp), 
  2347.                 MENU_VDEVICE0 + i, 
  2348.                 (S_OK == gcap.rgpmVideoMenu[i]->IsEqual(gcap.pmVideo)) ? MF_CHECKED : MF_UNCHECKED); 
  2349.  
  2350.     }
  2351.  
  2352.     for(i = 0; i < NUMELMS(gcap.rgpmAudioMenu); i++) {
  2353.         if (gcap.rgpmAudioMenu[i] == NULL)
  2354.             break;
  2355.         CheckMenuItem(  GetMenu(ghwndApp), 
  2356.                 MENU_ADEVICE0 + i, 
  2357.                 (S_OK == gcap.rgpmAudioMenu[i]->IsEqual(gcap.pmAudio)) ? MF_CHECKED : MF_UNCHECKED); 
  2358.  
  2359.     }
  2360.  
  2361.  
  2362.  
  2363.     // Put the video driver name in the status bar - if the filter supports
  2364.     // IAMVideoCompression::GetInfo, that's the best way to get the name and
  2365.     // the version.  Otherwise use the name we got from device enumeration
  2366.     // as a fallback.
  2367.     if (gcap.pVC) {
  2368.     HRESULT hr = gcap.pVC->GetInfo(wachVer, &versize, wachDesc, &descsize,
  2369.                             NULL, NULL, NULL, NULL);
  2370.     if (hr == S_OK) {
  2371.         wsprintf(tachStatus, TEXT("%s - %s"), W2T(wachDesc), W2T(wachVer));
  2372.         statusUpdateStatus(ghwndStatus, tachStatus);
  2373.         return;
  2374.     }
  2375.     }
  2376.     statusUpdateStatus(ghwndStatus, W2T(gcap.wachFriendlyName));
  2377. }
  2378.  
  2379. void ChooseDevices(char *szVideo, char *szAudio)
  2380. {
  2381.     WCHAR wszVideo[1024],  wszAudio[1024];
  2382.     MultiByteToWideChar(CP_ACP, 0, szVideo, -1, wszVideo, NUMELMS(wszVideo));
  2383.     MultiByteToWideChar(CP_ACP, 0, szAudio, -1, wszAudio, NUMELMS(wszAudio));
  2384.  
  2385.     IBindCtx *lpBC;
  2386.     HRESULT hr = CreateBindCtx(0, &lpBC);
  2387.     IMoniker *pmVideo = 0, *pmAudio = 0;
  2388.     if (SUCCEEDED(hr))
  2389.     {
  2390.         DWORD dwEaten;
  2391.         hr = MkParseDisplayName(lpBC, wszVideo, &dwEaten,
  2392.                                 &pmVideo);
  2393.         hr = MkParseDisplayName(lpBC, wszAudio, &dwEaten,
  2394.                                 &pmAudio);
  2395.  
  2396.         lpBC->Release();
  2397.     }
  2398.  
  2399.     // Handle the case where the video capture device used for the previous session
  2400.     // is not available now.
  2401.     BOOL bFound = FALSE;
  2402.     if (pmVideo != NULL)
  2403.     {
  2404.             for(int i = 0; i < NUMELMS(gcap.rgpmVideoMenu); i++) 
  2405.             {
  2406.                 if (gcap.rgpmVideoMenu[i] != NULL &&
  2407.                     S_OK == gcap.rgpmVideoMenu[i]->IsEqual(pmVideo))
  2408.                 {
  2409.                     bFound = TRUE;
  2410.                     break;
  2411.                 }
  2412.             }
  2413.     }
  2414.  
  2415.     if (!bFound)
  2416.     {
  2417.         if (gcap.iNumVCapDevices > 0)
  2418.         {
  2419.             IMonRelease(pmVideo);
  2420.             ASSERT(gcap.rgpmVideoMenu[0] != NULL);
  2421.             pmVideo = gcap.rgpmVideoMenu[0];
  2422.             pmVideo->AddRef();
  2423.         }
  2424.         else
  2425.             goto CleanUp;
  2426.     }
  2427.         
  2428.     ChooseDevices(pmVideo, pmAudio);
  2429.  
  2430. CleanUp:
  2431.     IMonRelease(pmVideo);
  2432.     IMonRelease(pmAudio);
  2433. }
  2434.  
  2435.  
  2436. // put all installed video and audio devices in the menus
  2437. //
  2438. void AddDevicesToMenu()
  2439. {
  2440.     USES_CONVERSION;
  2441.  
  2442.     if(gcap.fDeviceMenuPopulated) {
  2443.         return;
  2444.     }
  2445.     gcap.fDeviceMenuPopulated = true;
  2446.     gcap.iNumVCapDevices = 0;
  2447.     
  2448.     UINT    uIndex = 0;
  2449.     HMENU   hMenuSub;
  2450.     HRESULT hr;
  2451.     BOOL bCheck = FALSE;
  2452.  
  2453.     hMenuSub = GetSubMenu(GetMenu(ghwndApp), 1);        // Devices menu
  2454.  
  2455.     // Clean the sub menu
  2456.     int iMenuItems = GetMenuItemCount(hMenuSub);
  2457.     if (iMenuItems == -1)
  2458.     {
  2459.         ErrMsg("Error Cleaning Devices Menu");
  2460.         return;
  2461.     }
  2462.     else if (iMenuItems > 0)
  2463.     {
  2464.         for (int i = 0; i < iMenuItems; i++)
  2465.         {
  2466.             RemoveMenu(hMenuSub, 0, MF_BYPOSITION);
  2467.         }
  2468.  
  2469.     }
  2470.  
  2471.     {
  2472.         for(int i = 0; i < NUMELMS(gcap.rgpmVideoMenu); i++) {
  2473.             IMonRelease(gcap.rgpmVideoMenu[i]);
  2474.         }
  2475.         for( i = 0; i < NUMELMS(gcap.rgpmAudioMenu); i++) {
  2476.             IMonRelease(gcap.rgpmAudioMenu[i]);
  2477.         }
  2478.     }
  2479.  
  2480.  
  2481.     // enumerate all video capture devices
  2482.     ICreateDevEnum *pCreateDevEnum;
  2483.     hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER,
  2484.               IID_ICreateDevEnum, (void**)&pCreateDevEnum);
  2485.     if (hr != NOERROR)
  2486.     {
  2487.         ErrMsg("Error Creating Device Enumerator");
  2488.         return;
  2489.     }
  2490.  
  2491.     IEnumMoniker *pEm;
  2492.     hr = pCreateDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory,
  2493.                                 &pEm, 0);
  2494.     if (hr != NOERROR) {
  2495.     ErrMsg("Sorry, you have no video capture hardware");
  2496.     goto EnumAudio;
  2497.     }
  2498.     pEm->Reset();
  2499.     ULONG cFetched;
  2500.     IMoniker *pM;
  2501.     while(hr = pEm->Next(1, &pM, &cFetched), hr==S_OK)
  2502.     {
  2503.     IPropertyBag *pBag;
  2504.     hr = pM->BindToStorage(0, 0, IID_IPropertyBag, (void **)&pBag);
  2505.     if(SUCCEEDED(hr)) {
  2506.         VARIANT var;
  2507.         var.vt = VT_BSTR;
  2508.         hr = pBag->Read(L"FriendlyName", &var, NULL);
  2509.         if (hr == NOERROR) {
  2510.         AppendMenu(hMenuSub, MF_STRING, MENU_VDEVICE0 + uIndex,
  2511.                             W2T(var.bstrVal));
  2512.  
  2513.         if (gcap.pmVideo != 0 && (S_OK == gcap.pmVideo->IsEqual(pM)))
  2514.             bCheck = TRUE;
  2515.         CheckMenuItem(    hMenuSub, 
  2516.                         MENU_VDEVICE0 + uIndex, 
  2517.                         (bCheck ? MF_CHECKED : MF_UNCHECKED));
  2518.         bCheck = FALSE;
  2519.         EnableMenuItem(    hMenuSub,
  2520.                         MENU_VDEVICE0 + uIndex,
  2521.                         (gcap.fCapturing ? MF_DISABLED : MF_ENABLED));
  2522.                         
  2523.  
  2524.         SysFreeString(var.bstrVal);
  2525.  
  2526.                 ASSERT(gcap.rgpmVideoMenu[uIndex] == 0);
  2527.                 gcap.rgpmVideoMenu[uIndex] = pM;
  2528.                 pM->AddRef();
  2529.         }
  2530.         pBag->Release();
  2531.     }
  2532.     pM->Release();
  2533.     uIndex++;
  2534.     }
  2535.     pEm->Release();
  2536.  
  2537.     gcap.iNumVCapDevices = uIndex;
  2538.  
  2539.     // separate the video and audio devices
  2540.     AppendMenu(hMenuSub, MF_SEPARATOR, 0, NULL);
  2541.  
  2542. EnumAudio:
  2543.  
  2544.     // enumerate all audio capture devices
  2545.     uIndex = 0;
  2546.     bCheck = FALSE;
  2547.     
  2548.     ASSERT(pCreateDevEnum != NULL);
  2549.  
  2550.     hr = pCreateDevEnum->CreateClassEnumerator(CLSID_AudioInputDeviceCategory,
  2551.                                 &pEm, 0);
  2552.     pCreateDevEnum->Release();
  2553.     if (hr != NOERROR)
  2554.     return;
  2555.     pEm->Reset();
  2556.     while(hr = pEm->Next(1, &pM, &cFetched), hr==S_OK)
  2557.     {
  2558.     IPropertyBag *pBag;
  2559.     hr = pM->BindToStorage(0, 0, IID_IPropertyBag, (void **)&pBag);
  2560.     if(SUCCEEDED(hr)) {
  2561.         VARIANT var;
  2562.         var.vt = VT_BSTR;
  2563.         hr = pBag->Read(L"FriendlyName", &var, NULL);
  2564.         if (hr == NOERROR) {
  2565.         AppendMenu(hMenuSub, MF_STRING, MENU_ADEVICE0 + uIndex,
  2566.                             W2T(var.bstrVal));
  2567.  
  2568.         if (gcap.pmAudio != 0 && (S_OK == gcap.pmAudio->IsEqual(pM)))
  2569.             bCheck = TRUE;
  2570.         CheckMenuItem(    hMenuSub, 
  2571.                         MENU_ADEVICE0 + uIndex, 
  2572.                         (bCheck ? MF_CHECKED : MF_UNCHECKED));
  2573.         bCheck = FALSE;
  2574.         EnableMenuItem(    hMenuSub,
  2575.                         MENU_ADEVICE0 + uIndex,
  2576.                         (gcap.fCapturing ? MF_DISABLED : MF_ENABLED));
  2577.                         
  2578.         SysFreeString(var.bstrVal);
  2579.  
  2580.                 ASSERT(gcap.rgpmAudioMenu[uIndex] == 0);
  2581.                 gcap.rgpmAudioMenu[uIndex] = pM;
  2582.                 pM->AddRef();                
  2583.         }
  2584.         pBag->Release();
  2585.     }
  2586.     pM->Release();
  2587.     uIndex++;
  2588.     }
  2589.     pEm->Release();
  2590. }
  2591.  
  2592.  
  2593.  
  2594. // let them pick a frame rate
  2595. //
  2596. void ChooseFrameRate()
  2597. {
  2598.     double rate = gcap.FrameRate;
  2599.  
  2600.     DoDialog(ghwndApp, IDD_FrameRateDialog, (DLGPROC)FrameRateProc, 0);
  2601.  
  2602.     HRESULT hr = E_FAIL;
  2603.  
  2604.     // If somebody unchecks "use frame rate" it means we will no longer
  2605.     // tell the filter what frame rate to use... it will either continue
  2606.     // using the last one, or use some default, or if you bring up a dialog
  2607.     // box that has frame rate choices, it will obey them.
  2608.  
  2609.     // new frame rate?
  2610.     if (gcap.fUseFrameRate && gcap.FrameRate != rate) {
  2611.     if (gcap.fPreviewing)
  2612.         StopPreview();
  2613.     // now tell it what frame rate to capture at.  Just find the format it
  2614.     // is capturing with, and leave everything else alone
  2615.     if (gcap.pVSC) {
  2616.         AM_MEDIA_TYPE *pmt;
  2617.         hr = gcap.pVSC->GetFormat(&pmt);
  2618.         // DV capture does not use a VIDEOINFOHEADER
  2619.             if (hr == NOERROR) {
  2620.         if (pmt->formattype == FORMAT_VideoInfo) {
  2621.             VIDEOINFOHEADER *pvi = (VIDEOINFOHEADER *)pmt->pbFormat;
  2622.             pvi->AvgTimePerFrame =(LONGLONG)(10000000 / gcap.FrameRate);
  2623.                     hr = gcap.pVSC->SetFormat(pmt);
  2624.                     if (hr != S_OK)
  2625.                 ErrMsg("%x: Cannot set new frame rate", hr);
  2626.         }
  2627.         DeleteMediaType(pmt);
  2628.         }
  2629.     }
  2630.     if (hr != NOERROR)
  2631.         ErrMsg("Cannot set frame rate for capture");
  2632.     if (gcap.fWantPreview)  // we were previewing
  2633.         StartPreview();
  2634.     }
  2635. }
  2636.  
  2637.  
  2638. // let them set a capture time limit
  2639. //
  2640. void ChooseTimeLimit()
  2641. {
  2642.     DoDialog(ghwndApp, IDD_TimeLimitDialog, (DLGPROC)TimeLimitProc, 0);
  2643. }
  2644.  
  2645.  
  2646. // choose an audio capture format using ACM
  2647. //
  2648. void ChooseAudioFormat()
  2649. {
  2650.     ACMFORMATCHOOSE cfmt;
  2651.     DWORD dwSize;
  2652.     LPWAVEFORMATEX lpwfx;
  2653.     AM_MEDIA_TYPE *pmt;
  2654.  
  2655.     // there's no point if we can't set a new format
  2656.     if (gcap.pASC == NULL)
  2657.     return;
  2658.  
  2659.     // What's the largest format size around?
  2660.     acmMetrics(NULL, ACM_METRIC_MAX_SIZE_FORMAT, &dwSize);
  2661.     HRESULT hr = gcap.pASC->GetFormat(&pmt);
  2662.     if (hr != NOERROR)
  2663.     return;
  2664.     lpwfx = (LPWAVEFORMATEX)pmt->pbFormat;
  2665.     dwSize = max(dwSize, lpwfx->cbSize + sizeof(WAVEFORMATEX));
  2666.  
  2667.     // !!! This doesn't really map to the supported formats of the filter.
  2668.     // We should be using a property page based on IAMStreamConfig
  2669.  
  2670.     // Put up a dialog box initialized with the current format
  2671.     if (lpwfx = (LPWAVEFORMATEX)GlobalAllocPtr(GHND, dwSize)) {
  2672.     CopyMemory(lpwfx, pmt->pbFormat, pmt->cbFormat);
  2673.     _fmemset(&cfmt, 0, sizeof(ACMFORMATCHOOSE));
  2674.     cfmt.cbStruct = sizeof(ACMFORMATCHOOSE);
  2675.     cfmt.fdwStyle = ACMFORMATCHOOSE_STYLEF_INITTOWFXSTRUCT;
  2676.     // show only formats we can capture
  2677.     cfmt.fdwEnum = ACM_FORMATENUMF_HARDWARE | ACM_FORMATENUMF_INPUT;
  2678.     cfmt.hwndOwner = ghwndApp;
  2679.     cfmt.pwfx = lpwfx;
  2680.     cfmt.cbwfx = dwSize;
  2681.  
  2682.     // we chose a new format... so give it to the capture filter
  2683.     if (!acmFormatChoose(&cfmt)) {
  2684.         if (gcap.fPreviewing)
  2685.         StopPreview();  // can't call IAMStreamConfig::SetFormat
  2686.                 // while streaming
  2687.         ((CMediaType *)pmt)->SetFormat((LPBYTE)lpwfx,
  2688.                     lpwfx->cbSize + sizeof(WAVEFORMATEX));
  2689.         gcap.pASC->SetFormat(pmt);  // filter will reconnect
  2690.         if (gcap.fWantPreview)
  2691.         StartPreview();
  2692.     }
  2693.     GlobalFreePtr(lpwfx) ;
  2694.     }
  2695.     DeleteMediaType(pmt);
  2696. }
  2697.  
  2698.  
  2699. /*----------------------------------------------------------------------------*\
  2700. |    AppCommand()
  2701. |
  2702. |    Process all of our WM_COMMAND messages.
  2703. \*----------------------------------------------------------------------------*/
  2704. LONG PASCAL AppCommand (HWND hwnd, unsigned msg, WPARAM wParam, LPARAM lParam)
  2705. {
  2706.     HRESULT hr;
  2707.     int id = GET_WM_COMMAND_ID(wParam, lParam);
  2708.     switch(id)
  2709.     {
  2710.     //
  2711.     // Our about box
  2712.     //
  2713.     case MENU_ABOUT:
  2714.         DialogBox(ghInstApp, MAKEINTRESOURCE(IDD_ABOUT), hwnd, 
  2715.                             (DLGPROC)AboutDlgProc);
  2716.         break;
  2717.  
  2718.     //
  2719.     // We want out of here!
  2720.     //
  2721.     case MENU_EXIT:
  2722.         PostMessage(hwnd,WM_CLOSE,0,0L);
  2723.         break;
  2724.  
  2725.     // choose a capture file
  2726.     //
  2727.     case MENU_SET_CAP_FILE:
  2728.         SetCaptureFile(hwnd);
  2729.         break;
  2730.  
  2731.     // pre-allocate the capture file
  2732.     //
  2733.     case MENU_ALLOC_CAP_FILE:
  2734.         AllocCaptureFile(hwnd);
  2735.         break;
  2736.  
  2737.     // save the capture file
  2738.     //
  2739.     case MENU_SAVE_CAP_FILE:
  2740.         SaveCaptureFile(hwnd);
  2741.         break;
  2742.  
  2743.     // start capturing
  2744.     //
  2745.     case MENU_START_CAP:
  2746.         if (gcap.fPreviewing)
  2747.         StopPreview();
  2748.         if (gcap.fPreviewGraphBuilt)
  2749.         TearDownGraph();
  2750.         BuildCaptureGraph();
  2751.         StartCapture();
  2752.         break;
  2753.  
  2754.     // toggle preview
  2755.     // 
  2756.     case MENU_PREVIEW:
  2757.         gcap.fWantPreview = !gcap.fWantPreview;
  2758.         if (gcap.fWantPreview) {
  2759.         BuildPreviewGraph();
  2760.         StartPreview();
  2761.         } else
  2762.         StopPreview();
  2763.         break;
  2764.  
  2765.     // stop capture
  2766.     //
  2767.     case MENU_STOP_CAP:
  2768.         StopCapture();
  2769.         if (gcap.fWantPreview) {
  2770.         BuildPreviewGraph();
  2771.         StartPreview();
  2772.         }
  2773.         break;
  2774.  
  2775.     // select the master stream
  2776.     //
  2777.     case MENU_NOMASTER:
  2778.         gcap.iMasterStream = -1;
  2779.         if (gcap.pConfigAviMux) {
  2780.         hr = gcap.pConfigAviMux->SetMasterStream(gcap.iMasterStream);
  2781.         if (hr != NOERROR)
  2782.             ErrMsg("SetMasterStream failed!");
  2783.         }
  2784.         break;
  2785.     case MENU_AUDIOMASTER:
  2786.         gcap.iMasterStream = 1;
  2787.         if (gcap.pConfigAviMux) {
  2788.         hr = gcap.pConfigAviMux->SetMasterStream(gcap.iMasterStream);
  2789.         if (hr != NOERROR)
  2790.             ErrMsg("SetMasterStream failed!");
  2791.         }
  2792.         break;
  2793.     case MENU_VIDEOMASTER:
  2794.         gcap.iMasterStream = 0;
  2795.         if (gcap.pConfigAviMux) {
  2796.         hr = gcap.pConfigAviMux->SetMasterStream(gcap.iMasterStream);
  2797.         if (hr != NOERROR)
  2798.             ErrMsg("SetMasterStream failed!");
  2799.         }
  2800.         break;
  2801.  
  2802.     // toggle capturing audio
  2803.     case MENU_CAP_AUDIO:
  2804.         if (gcap.fPreviewing)
  2805.         StopPreview();
  2806.         gcap.fCapAudio = !gcap.fCapAudio;
  2807.         // when we capture we'll need a different graph now
  2808.         if (gcap.fCaptureGraphBuilt || gcap.fPreviewGraphBuilt)
  2809.         TearDownGraph();
  2810.         if (gcap.fWantPreview) {
  2811.         BuildPreviewGraph();
  2812.         StartPreview();
  2813.         }
  2814.         break;
  2815.  
  2816.     // toggle closed captioning
  2817.     case MENU_CAP_CC:
  2818.         if (gcap.fPreviewing)
  2819.         StopPreview();
  2820.         gcap.fCapCC = !gcap.fCapCC;
  2821.         // when we capture we'll need a different graph now
  2822.         if (gcap.fCaptureGraphBuilt || gcap.fPreviewGraphBuilt)
  2823.         TearDownGraph();
  2824.         if (gcap.fWantPreview) {
  2825.         BuildPreviewGraph();
  2826.         StartPreview();
  2827.         }
  2828.         break;
  2829.  
  2830.     // choose the audio capture format
  2831.     //
  2832.     case MENU_AUDIOFORMAT:
  2833.         ChooseAudioFormat();
  2834.         break;
  2835.  
  2836.     // pick a frame rate
  2837.     //
  2838.     case MENU_FRAMERATE:
  2839.         ChooseFrameRate();
  2840.         break;
  2841.  
  2842.     // pick a time limit
  2843.     //
  2844.     case MENU_TIMELIMIT:
  2845.         ChooseTimeLimit();
  2846.         break;
  2847.  
  2848.     // pick which video capture device to use
  2849.     // pick which video capture device to use
  2850.     //
  2851.     case MENU_VDEVICE0:
  2852.     case MENU_VDEVICE1:
  2853.     case MENU_VDEVICE2:
  2854.     case MENU_VDEVICE3:
  2855.     case MENU_VDEVICE4:
  2856.     case MENU_VDEVICE5:
  2857.     case MENU_VDEVICE6:
  2858.     case MENU_VDEVICE7:
  2859.     case MENU_VDEVICE8:
  2860.     case MENU_VDEVICE9:
  2861.         ChooseDevices(gcap.rgpmVideoMenu[id - MENU_VDEVICE0], gcap.pmAudio);
  2862.         break;
  2863.  
  2864.     // pick which audio capture device to use
  2865.     //
  2866.     case MENU_ADEVICE0:
  2867.     case MENU_ADEVICE1:
  2868.     case MENU_ADEVICE2:
  2869.     case MENU_ADEVICE3:
  2870.     case MENU_ADEVICE4:
  2871.     case MENU_ADEVICE5:
  2872.     case MENU_ADEVICE6:
  2873.     case MENU_ADEVICE7:
  2874.     case MENU_ADEVICE8:
  2875.     case MENU_ADEVICE9:
  2876.         ChooseDevices(gcap.pmVideo, gcap.rgpmAudioMenu[id - MENU_ADEVICE0]);
  2877.         break;
  2878.  
  2879.     // video format dialog
  2880.     //
  2881.     case MENU_DIALOG0:
  2882.     case MENU_DIALOG1:
  2883.     case MENU_DIALOG2:
  2884.     case MENU_DIALOG3:
  2885.     case MENU_DIALOG4:
  2886.     case MENU_DIALOG5:
  2887.     case MENU_DIALOG6:
  2888.     case MENU_DIALOG7:
  2889.     case MENU_DIALOG8:
  2890.     case MENU_DIALOG9:
  2891.     case MENU_DIALOGA:
  2892.     case MENU_DIALOGB:
  2893.     case MENU_DIALOGC:
  2894.     case MENU_DIALOGD:
  2895.     case MENU_DIALOGE:
  2896.     case MENU_DIALOGF:
  2897.  
  2898.          // they want the VfW format dialog
  2899.         if (id - MENU_DIALOG0 == gcap.iFormatDialogPos) {
  2900.         // this dialog will not work while previewing
  2901.         if (gcap.fWantPreview)
  2902.             StopPreview();
  2903.         HRESULT hrD;
  2904.             hrD = gcap.pDlg->ShowDialog(VfwCaptureDialog_Format, ghwndApp);
  2905.         // Oh uh!  Sometimes bringing up the FORMAT dialog can result
  2906.         // in changing to a capture format that the current graph 
  2907.         // can't handle.  It looks like that has happened and we'll
  2908.         // have to rebuild the graph.
  2909.         if (hrD == VFW_E_CANNOT_CONNECT) {
  2910.                 DbgLog((LOG_TRACE,1,TEXT("DIALOG CORRUPTED GRAPH!")));
  2911.             TearDownGraph();    // now we need to rebuild
  2912.             // !!! This won't work if we've left a stranded h/w codec
  2913.         }
  2914.  
  2915.         // Resize our window to be the same size that we're capturing
  2916.             if (gcap.pVSC) {
  2917.             AM_MEDIA_TYPE *pmt;
  2918.             // get format being used NOW
  2919.             hr = gcap.pVSC->GetFormat(&pmt);
  2920.                 // DV capture does not use a VIDEOINFOHEADER
  2921.                     if (hr == NOERROR) {
  2922.              if (pmt->formattype == FORMAT_VideoInfo) {
  2923.                     // resize our window to the new capture size
  2924.                     ResizeWindow(HEADER(pmt->pbFormat)->biWidth,
  2925.                     abs(HEADER(pmt->pbFormat)->biHeight));
  2926.             }
  2927.                 DeleteMediaType(pmt);
  2928.             }
  2929.             }
  2930.  
  2931.             if (gcap.fWantPreview) {
  2932.             BuildPreviewGraph();
  2933.             StartPreview();
  2934.         }
  2935.         } else if (id - MENU_DIALOG0 == gcap.iSourceDialogPos) {
  2936.         // this dialog will not work while previewing
  2937.         if (gcap.fWantPreview)
  2938.             StopPreview();
  2939.             gcap.pDlg->ShowDialog(VfwCaptureDialog_Source, ghwndApp);
  2940.             if (gcap.fWantPreview)
  2941.             StartPreview();
  2942.         } else if (id - MENU_DIALOG0 == gcap.iDisplayDialogPos) {
  2943.         // this dialog will not work while previewing
  2944.         if (gcap.fWantPreview)
  2945.             StopPreview();
  2946.             gcap.pDlg->ShowDialog(VfwCaptureDialog_Display, ghwndApp);
  2947.             if (gcap.fWantPreview)
  2948.             StartPreview();
  2949.  
  2950.         // now the code for the new dialogs
  2951.  
  2952.         } else if (id - MENU_DIALOG0 == gcap.iVCapDialogPos) {
  2953.         ISpecifyPropertyPages *pSpec;
  2954.         CAUUID cauuid;
  2955.                 hr = gcap.pVCap->QueryInterface(IID_ISpecifyPropertyPages,
  2956.                             (void **)&pSpec);
  2957.             if (hr == S_OK) {
  2958.                 hr = pSpec->GetPages(&cauuid);
  2959.                     hr = OleCreatePropertyFrame(ghwndApp, 30, 30, NULL, 1,
  2960.                     (IUnknown **)&gcap.pVCap, cauuid.cElems,
  2961.             (GUID *)cauuid.pElems, 0, 0, NULL);
  2962.             CoTaskMemFree(cauuid.pElems);
  2963.             pSpec->Release();
  2964.         }
  2965.  
  2966.         } else if (id - MENU_DIALOG0 == gcap.iVCapCapturePinDialogPos) {
  2967.         // You can change this pin's output format in these dialogs.
  2968.         // If the capture pin is already connected to somebody who's 
  2969.         // fussy about the connection type, that may prevent using 
  2970.         // this dialog(!) because the filter it's connected to might not
  2971.         // allow reconnecting to a new format. (EG: you switch from RGB
  2972.                 // to some compressed type, and need to pull in a decoder)
  2973.         // I need to tear down the graph downstream of the
  2974.         // capture filter before bringing up these dialogs.
  2975.         // In any case, the graph must be STOPPED when calling them.
  2976.         if (gcap.fWantPreview)
  2977.             StopPreview();    // make sure graph is stopped
  2978.             // The capture pin that we are trying to set the format on is connected if
  2979.             // one of these variable is set to TRUE. The pin should be disconnected for
  2980.             // the dialog to work properly.
  2981.             if (gcap.fCaptureGraphBuilt || gcap.fPreviewGraphBuilt) {
  2982.                 DbgLog((LOG_TRACE,1,TEXT("Tear down graph for dialog")));
  2983.                 TearDownGraph();    // graph could prevent dialog working
  2984.             }
  2985.             IAMStreamConfig *pSC;
  2986.             hr = gcap.pBuilder->FindInterface(&PIN_CATEGORY_CAPTURE,
  2987.                 &MEDIATYPE_Interleaved, gcap.pVCap,
  2988.                 IID_IAMStreamConfig, (void **)&pSC);
  2989.          if (hr != NOERROR)
  2990.                 hr = gcap.pBuilder->FindInterface(&PIN_CATEGORY_CAPTURE,
  2991.                 &MEDIATYPE_Video, gcap.pVCap,
  2992.                 IID_IAMStreamConfig, (void **)&pSC);
  2993.         ISpecifyPropertyPages *pSpec;
  2994.         CAUUID cauuid;
  2995.                 hr = pSC->QueryInterface(IID_ISpecifyPropertyPages,
  2996.                             (void **)&pSpec);
  2997.             if (hr == S_OK) {
  2998.                 hr = pSpec->GetPages(&cauuid);
  2999.                     hr = OleCreatePropertyFrame(ghwndApp, 30, 30, NULL, 1,
  3000.                     (IUnknown **)&pSC, cauuid.cElems,
  3001.             (GUID *)cauuid.pElems, 0, 0, NULL);
  3002.  
  3003.             // !!! What if changing output formats couldn't reconnect
  3004.             // and the graph is broken?  Shouldn't be possible...
  3005.         
  3006.                 if (gcap.pVSC) {
  3007.                 AM_MEDIA_TYPE *pmt;
  3008.                 // get format being used NOW
  3009.                 hr = gcap.pVSC->GetFormat(&pmt);
  3010.                     // DV capture does not use a VIDEOINFOHEADER
  3011.                         if (hr == NOERROR) {
  3012.                  if (pmt->formattype == FORMAT_VideoInfo) {
  3013.                         // resize our window to the new capture size
  3014.                         ResizeWindow(HEADER(pmt->pbFormat)->biWidth,
  3015.                       abs(HEADER(pmt->pbFormat)->biHeight));
  3016.                 }
  3017.                     DeleteMediaType(pmt);
  3018.                 }
  3019.                 }
  3020.  
  3021.             CoTaskMemFree(cauuid.pElems);
  3022.             pSpec->Release();
  3023.         }
  3024.         pSC->Release();
  3025.             if (gcap.fWantPreview) {
  3026.             BuildPreviewGraph();
  3027.             StartPreview();
  3028.         }
  3029.  
  3030.  
  3031.         } else if (id - MENU_DIALOG0 == gcap.iVCapPreviewPinDialogPos) {
  3032.         // this dialog may not work if the preview pin is connected
  3033.         // already, because the downstream filter may reject a format
  3034.         // change, so we better kill the graph. (EG: We switch from 
  3035.                 // capturing RGB to some compressed fmt, and need to pull in
  3036.                 // a decompressor)
  3037.         if (gcap.fWantPreview) {
  3038.             StopPreview();
  3039.             TearDownGraph();
  3040.         }
  3041.             IAMStreamConfig *pSC;
  3042.         // This dialog changes the preview format, so it might affect
  3043.         // the format being drawn.  Our app's window size is taken
  3044.         // from the size of the capture pin's video, not the preview
  3045.         // pin, so changing that here won't have any effect. All in all,
  3046.         // this probably won't be a terribly useful dialog in this app.
  3047.             hr = gcap.pBuilder->FindInterface(&PIN_CATEGORY_PREVIEW,
  3048.                 &MEDIATYPE_Interleaved, gcap.pVCap,
  3049.                 IID_IAMStreamConfig, (void **)&pSC);
  3050.         if (hr != NOERROR)
  3051.                 hr = gcap.pBuilder->FindInterface(&PIN_CATEGORY_PREVIEW,
  3052.                 &MEDIATYPE_Video, gcap.pVCap,
  3053.                 IID_IAMStreamConfig, (void **)&pSC);
  3054.         ISpecifyPropertyPages *pSpec;
  3055.         CAUUID cauuid;
  3056.                 hr = pSC->QueryInterface(IID_ISpecifyPropertyPages,
  3057.                             (void **)&pSpec);
  3058.             if (hr == S_OK) {
  3059.                 hr = pSpec->GetPages(&cauuid);
  3060.                     hr = OleCreatePropertyFrame(ghwndApp, 30, 30, NULL, 1,
  3061.                     (IUnknown **)&pSC, cauuid.cElems,
  3062.             (GUID *)cauuid.pElems, 0, 0, NULL);
  3063.             CoTaskMemFree(cauuid.pElems);
  3064.             pSpec->Release();
  3065.         }
  3066.         pSC->Release();
  3067.         if (gcap.fWantPreview) {
  3068.             BuildPreviewGraph();
  3069.             StartPreview();
  3070.         }
  3071.  
  3072.         } else if (id - MENU_DIALOG0 == gcap.iVCrossbarDialogPos) {
  3073.             IAMCrossbar *pX;
  3074.             hr = gcap.pBuilder->FindInterface(&PIN_CATEGORY_CAPTURE,
  3075.                 &MEDIATYPE_Interleaved, gcap.pVCap,
  3076.                 IID_IAMCrossbar, (void **)&pX);
  3077.         if (hr != NOERROR)
  3078.                 hr = gcap.pBuilder->FindInterface(&PIN_CATEGORY_CAPTURE,
  3079.                 &MEDIATYPE_Video, gcap.pVCap,
  3080.                 IID_IAMCrossbar, (void **)&pX);
  3081.         ISpecifyPropertyPages *pSpec;
  3082.         CAUUID cauuid;
  3083.                 hr = pX->QueryInterface(IID_ISpecifyPropertyPages,
  3084.                             (void **)&pSpec);
  3085.             if (hr == S_OK) {
  3086.                 hr = pSpec->GetPages(&cauuid);
  3087.                     hr = OleCreatePropertyFrame(ghwndApp, 30, 30, NULL, 1,
  3088.                     (IUnknown **)&pX, cauuid.cElems,
  3089.             (GUID *)cauuid.pElems, 0, 0, NULL);
  3090.             CoTaskMemFree(cauuid.pElems);
  3091.             pSpec->Release();
  3092.         }
  3093.         pX->Release();
  3094.  
  3095.         } else if (id - MENU_DIALOG0 == gcap.iTVTunerDialogPos) {
  3096.             IAMTVTuner *pTV;
  3097.             hr = gcap.pBuilder->FindInterface(&PIN_CATEGORY_CAPTURE,
  3098.                 &MEDIATYPE_Interleaved, gcap.pVCap,
  3099.                 IID_IAMTVTuner, (void **)&pTV);
  3100.         if (hr != NOERROR)
  3101.                 hr = gcap.pBuilder->FindInterface(&PIN_CATEGORY_CAPTURE,
  3102.                 &MEDIATYPE_Video, gcap.pVCap,
  3103.                 IID_IAMTVTuner, (void **)&pTV);
  3104.         ISpecifyPropertyPages *pSpec;
  3105.         CAUUID cauuid;
  3106.                 hr = pTV->QueryInterface(IID_ISpecifyPropertyPages,
  3107.                             (void **)&pSpec);
  3108.             if (hr == S_OK) {
  3109.                 hr = pSpec->GetPages(&cauuid);
  3110.                     hr = OleCreatePropertyFrame(ghwndApp, 30, 30, NULL, 1,
  3111.                     (IUnknown **)&pTV, cauuid.cElems,
  3112.             (GUID *)cauuid.pElems, 0, 0, NULL);
  3113.             CoTaskMemFree(cauuid.pElems);
  3114.             pSpec->Release();
  3115.         }
  3116.         pTV->Release();
  3117.  
  3118.         } else if (id - MENU_DIALOG0 == gcap.iACapDialogPos) {
  3119.         ISpecifyPropertyPages *pSpec;
  3120.         CAUUID cauuid;
  3121.                 hr = gcap.pACap->QueryInterface(IID_ISpecifyPropertyPages,
  3122.                             (void **)&pSpec);
  3123.             if (hr == S_OK) {
  3124.                 hr = pSpec->GetPages(&cauuid);
  3125.                     hr = OleCreatePropertyFrame(ghwndApp, 30, 30, NULL, 1,
  3126.                     (IUnknown **)&gcap.pACap, cauuid.cElems,
  3127.             (GUID *)cauuid.pElems, 0, 0, NULL);
  3128.             CoTaskMemFree(cauuid.pElems);
  3129.             pSpec->Release();
  3130.         }
  3131.  
  3132.         } else if (id - MENU_DIALOG0 == gcap.iACapCapturePinDialogPos) {
  3133.         // this dialog will not work while previewing - it might change
  3134.         // the output format!
  3135.         if (gcap.fWantPreview)
  3136.             StopPreview();
  3137.             IAMStreamConfig *pSC;
  3138.             hr = gcap.pBuilder->FindInterface(&PIN_CATEGORY_CAPTURE,
  3139.                 &MEDIATYPE_Audio, gcap.pACap,
  3140.                 IID_IAMStreamConfig, (void **)&pSC);
  3141.         ISpecifyPropertyPages *pSpec;
  3142.         CAUUID cauuid;
  3143.                 hr = pSC->QueryInterface(IID_ISpecifyPropertyPages,
  3144.                             (void **)&pSpec);
  3145.             if (hr == S_OK) {
  3146.                 hr = pSpec->GetPages(&cauuid);
  3147.                     hr = OleCreatePropertyFrame(ghwndApp, 30, 30, NULL, 1,
  3148.                     (IUnknown **)&pSC, cauuid.cElems,
  3149.             (GUID *)cauuid.pElems, 0, 0, NULL);
  3150.             CoTaskMemFree(cauuid.pElems);
  3151.             pSpec->Release();
  3152.         }
  3153.         pSC->Release();
  3154.             if (gcap.fWantPreview)
  3155.             StartPreview();
  3156.  
  3157.         } else if (id - MENU_DIALOG0 == gcap.iACrossbarDialogPos) {
  3158.             IAMCrossbar *pX, *pX2;
  3159.         IBaseFilter *pXF;
  3160.         // we could use better error checking here... I'm assuming
  3161.         // this won't fail
  3162.             hr = gcap.pBuilder->FindInterface(&PIN_CATEGORY_CAPTURE,
  3163.                 &MEDIATYPE_Interleaved, gcap.pVCap,
  3164.                 IID_IAMCrossbar, (void **)&pX);
  3165.         if (hr != NOERROR)
  3166.                 hr = gcap.pBuilder->FindInterface(&PIN_CATEGORY_CAPTURE,
  3167.                 &MEDIATYPE_Video, gcap.pVCap,
  3168.                 IID_IAMCrossbar, (void **)&pX);
  3169.         hr = pX->QueryInterface(IID_IBaseFilter, (void **)&pXF);
  3170.             hr = gcap.pBuilder->FindInterface(&LOOK_UPSTREAM_ONLY, NULL,
  3171.                 pXF, IID_IAMCrossbar, (void **)&pX2);
  3172.         ISpecifyPropertyPages *pSpec;
  3173.         CAUUID cauuid;
  3174.                 hr = pX2->QueryInterface(IID_ISpecifyPropertyPages,
  3175.                             (void **)&pSpec);
  3176.             if (hr == S_OK) {
  3177.                 hr = pSpec->GetPages(&cauuid);
  3178.                     hr = OleCreatePropertyFrame(ghwndApp, 30, 30, NULL, 1,
  3179.                     (IUnknown **)&pX2, cauuid.cElems,
  3180.             (GUID *)cauuid.pElems, 0, 0, NULL);
  3181.             CoTaskMemFree(cauuid.pElems);
  3182.             pSpec->Release();
  3183.         }
  3184.         pX2->Release();
  3185.         pXF->Release();
  3186.         pX->Release();
  3187.  
  3188.         } else if (id - MENU_DIALOG0 == gcap.iTVAudioDialogPos) {
  3189.             IAMTVAudio *pTVA;
  3190.             hr = gcap.pBuilder->FindInterface(&PIN_CATEGORY_CAPTURE,
  3191.                 &MEDIATYPE_Audio, gcap.pACap,
  3192.                 IID_IAMTVAudio, (void **)&pTVA);
  3193.         ISpecifyPropertyPages *pSpec;
  3194.         CAUUID cauuid;
  3195.                 hr = pTVA->QueryInterface(IID_ISpecifyPropertyPages,
  3196.                             (void **)&pSpec);
  3197.             if (hr == S_OK) {
  3198.                 hr = pSpec->GetPages(&cauuid);
  3199.                     hr = OleCreatePropertyFrame(ghwndApp, 30, 30, NULL, 1,
  3200.                     (IUnknown **)&pTVA, cauuid.cElems,
  3201.             (GUID *)cauuid.pElems, 0, 0, NULL);
  3202.             CoTaskMemFree(cauuid.pElems);
  3203.             pSpec->Release();
  3204.         }
  3205.         pTVA->Release();
  3206.  
  3207.         } else if (((id - MENU_DIALOG0) >  gcap.iVideoInputMenuPos) && 
  3208.                     (id - MENU_DIALOG0) <= gcap.iVideoInputMenuPos + gcap.NumberOfVideoInputs) {
  3209.             // Remove existing checks
  3210.             for (int j = 0; j < gcap.NumberOfVideoInputs; j++) {
  3211.  
  3212.                 CheckMenuItem(gcap.hMenuPopup, j, MF_BYPOSITION | 
  3213.                               ((j == (id - MENU_DIALOG0) - gcap.iVideoInputMenuPos - 1) ?
  3214.                               MF_CHECKED : MF_UNCHECKED )); 
  3215.             }
  3216.  
  3217.             if (gcap.pCrossbar) {
  3218.                 EXECUTE_ASSERT (S_OK == gcap.pCrossbar->SetInputIndex ((id - MENU_DIALOG0) - gcap.iVideoInputMenuPos - 1));
  3219.             }
  3220.  
  3221.     }
  3222.  
  3223.     break;
  3224.  
  3225.     }
  3226.     return 0L;
  3227. }
  3228.  
  3229.  
  3230. /*----------------------------------------------------------------------------*\
  3231. |   ErrMsg - Opens a Message box with a error message in it.  The user can     |
  3232. |            select the OK button to continue                                  |
  3233. \*----------------------------------------------------------------------------*/
  3234. int ErrMsg (LPTSTR sz,...)
  3235. {
  3236.     static TCHAR tach[2000];
  3237.     va_list va;
  3238.  
  3239.     va_start(va, sz);
  3240.     wvsprintf (tach, sz, va);
  3241.     va_end(va);
  3242.     MessageBox(ghwndApp, tach, NULL, MB_OK|MB_ICONEXCLAMATION|MB_TASKMODAL);
  3243.     return FALSE;
  3244. }
  3245.  
  3246.  
  3247. /* AboutDlgProc()
  3248.  *
  3249.  * Dialog Procedure for the "about" dialog box.
  3250.  *
  3251.  */
  3252.  
  3253. BOOL CALLBACK AboutDlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
  3254. {
  3255.     switch (msg) {
  3256.     case WM_COMMAND:
  3257.         EndDialog(hwnd, TRUE);
  3258.         return TRUE;
  3259.     case WM_INITDIALOG:
  3260.         return TRUE;
  3261.     }
  3262.     return FALSE;
  3263. }
  3264.  
  3265.  
  3266. // pre-allocate the capture file
  3267. //
  3268. BOOL AllocCaptureFile(HWND hWnd)
  3269. {
  3270.     USES_CONVERSION;
  3271.  
  3272.     // we'll get into an infinite loop in the dlg proc setting a value
  3273.     if (gcap.szCaptureFile[0] == 0)
  3274.     return FALSE;
  3275.  
  3276.     /*
  3277.      * show the allocate file space dialog to encourage
  3278.      * the user to pre-allocate space
  3279.      */
  3280.     if (DoDialog(hWnd, IDD_AllocCapFileSpace, (DLGPROC)AllocCapFileProc, 0)) {
  3281.  
  3282.     // ensure repaint after dismissing dialog before
  3283.     // possibly lengthy operation
  3284.     UpdateWindow(ghwndApp);
  3285.  
  3286.     // User has hit OK. Alloc requested capture file space
  3287.     BOOL f = MakeBuilder();
  3288.     if (!f)
  3289.         return FALSE;
  3290.     if (gcap.pBuilder->AllocCapFile(T2W(gcap.szCaptureFile),
  3291.         (DWORDLONG)gcap.wCapFileSize * 1024L * 1024L) != NOERROR) {
  3292.         MessageBox(ghwndApp, TEXT("Error"),
  3293.             TEXT("Failed to pre-allocate capture file space"),
  3294.             MB_OK | MB_ICONEXCLAMATION);
  3295.         return FALSE;
  3296.     }
  3297.     return TRUE;
  3298.     } else {
  3299.     return FALSE;
  3300.     }
  3301. }
  3302.  
  3303.  
  3304. /*
  3305.  * Put up the open file dialog
  3306.  */
  3307. BOOL OpenFileDialog(HWND hWnd, LPTSTR pszName, int cb)
  3308. {
  3309.     OPENFILENAME ofn;
  3310.     LPTSTR p;
  3311.     TCHAR        szFileName[_MAX_PATH];
  3312.     TCHAR        szBuffer[_MAX_PATH] ;
  3313.  
  3314.     if (pszName == NULL || cb <= 0)
  3315.     return FALSE;
  3316.  
  3317.     // start with capture file as current file name
  3318.     szFileName[0] = 0;
  3319.     lstrcpy(szFileName, gcap.szCaptureFile);
  3320.  
  3321.     // Get just the path info
  3322.     // Terminate the full path at the last backslash
  3323.     lstrcpy(szBuffer, szFileName);
  3324.     for (p = szBuffer + lstrlen(szBuffer); p > szBuffer; p--) {
  3325.     if (*p == '\\') {
  3326.         *(p+1) = '\0';
  3327.         break;
  3328.     }
  3329.     }
  3330.  
  3331.     _fmemset(&ofn, 0, sizeof(OPENFILENAME)) ;
  3332.     ofn.lStructSize = sizeof(OPENFILENAME) ;
  3333.     ofn.hwndOwner = hWnd ;
  3334.     ofn.lpstrFilter = "Microsoft AVI\0*.avi\0\0";
  3335.     ofn.nFilterIndex = 0 ;
  3336.     ofn.lpstrFile = szFileName;
  3337.     ofn.nMaxFile = sizeof(szFileName) ;
  3338.     ofn.lpstrFileTitle = NULL;
  3339.     ofn.lpstrTitle = "Set Capture File";
  3340.     ofn.nMaxFileTitle = 0 ;
  3341.     ofn.lpstrInitialDir = szBuffer;
  3342.     ofn.Flags = OFN_HIDEREADONLY | OFN_NOREADONLYRETURN | OFN_PATHMUSTEXIST ;
  3343.  
  3344.     if (GetOpenFileName(&ofn)) {
  3345.     // We have a capture file name
  3346.     lstrcpyn(pszName, szFileName, cb);
  3347.     return TRUE;
  3348.     } else {
  3349.     return FALSE;
  3350.     }
  3351. }
  3352.  
  3353.  
  3354. /*
  3355.  * Put up a dialog to allow the user to select a capture file.
  3356.  */
  3357. BOOL SetCaptureFile(HWND hWnd)
  3358. {
  3359.     USES_CONVERSION;
  3360.  
  3361.     if (OpenFileDialog(hWnd, gcap.szCaptureFile, _MAX_PATH)) {
  3362.     OFSTRUCT os;
  3363.  
  3364.     // We have a capture file name
  3365.  
  3366.     /*
  3367.      * if this is a new file, then invite the user to
  3368.      * allocate some space
  3369.      */
  3370.     if (OpenFile(gcap.szCaptureFile, &os, OF_EXIST) == HFILE_ERROR) {
  3371.  
  3372.         // bring up dialog, and set new file size
  3373.         BOOL f = AllocCaptureFile(hWnd);
  3374.         if (!f)
  3375.         return FALSE;
  3376.     }
  3377.     } else {
  3378.     return FALSE;
  3379.     }
  3380.  
  3381.     SetAppCaption();    // new a new app caption
  3382.  
  3383.     // tell the file writer to use the new filename
  3384.     if (gcap.pSink) {
  3385.     gcap.pSink->SetFileName(T2W(gcap.szCaptureFile), NULL);
  3386.     }
  3387.  
  3388.     return TRUE;
  3389. }
  3390.  
  3391.  
  3392. /*
  3393.  * Put up a dialog to allow the user to save the contents of the capture file
  3394.  * elsewhere
  3395.  */
  3396. BOOL SaveCaptureFile(HWND hWnd)
  3397. {
  3398.     USES_CONVERSION;
  3399.     HRESULT hr;
  3400.     TCHAR tachDstFile[_MAX_PATH];
  3401.  
  3402.     if (gcap.pBuilder == NULL)
  3403.     return FALSE;
  3404.  
  3405.     if (OpenFileDialog(hWnd, tachDstFile, _MAX_PATH)) {
  3406.  
  3407.     // We have a capture file name
  3408.     statusUpdateStatus(ghwndStatus, TEXT("Saving capture file - please wait..."));
  3409.  
  3410.     // we need our own graph builder because the main one might not exist
  3411.     ICaptureGraphBuilder2 *pBuilder;
  3412.     hr = CoCreateInstance((REFCLSID)CLSID_CaptureGraphBuilder2,
  3413.             NULL, CLSCTX_INPROC, (REFIID)IID_ICaptureGraphBuilder2,
  3414.             (void **)&pBuilder);
  3415.  
  3416.     if (hr == NOERROR) {
  3417.         // allow the user to press ESC to abort... ask for progress
  3418.         CProgress *pProg = new CProgress(TEXT(""), NULL, &hr);
  3419.         IAMCopyCaptureFileProgress *pIProg = NULL;
  3420.         if (pProg) {
  3421.             hr = pProg->QueryInterface(IID_IAMCopyCaptureFileProgress,
  3422.                                             (void **)&pIProg);
  3423.         }
  3424.         hr = pBuilder->CopyCaptureFile(T2W(gcap.szCaptureFile),
  3425.                                        T2W(tachDstFile), TRUE, pIProg);
  3426.         if (pIProg)
  3427.             pIProg->Release();
  3428.         pBuilder->Release();
  3429.     }
  3430.  
  3431.     if (hr == S_OK)
  3432.         statusUpdateStatus(ghwndStatus, "Capture file saved");
  3433.     else if (hr == S_FALSE)
  3434.         statusUpdateStatus(ghwndStatus, "Capture file save aborted");
  3435.     else
  3436.         statusUpdateStatus(ghwndStatus, "Capture file save ERROR");
  3437.     return (hr == NOERROR ? TRUE : FALSE); 
  3438.  
  3439.     } else {
  3440.     return TRUE;    // they cancelled or something
  3441.     }
  3442. }
  3443.  
  3444. // brings up a dialog box
  3445. //
  3446. int DoDialog(HWND hwndParent, int DialogID, DLGPROC fnDialog, long lParam)
  3447. {
  3448.     DLGPROC fn;
  3449.     int result;
  3450.  
  3451.     fn = (DLGPROC)MakeProcInstance(fnDialog, ghInstApp);
  3452.     result = DialogBoxParam(
  3453.         ghInstApp,
  3454.         MAKEINTRESOURCE(DialogID),
  3455.         hwndParent,
  3456.         fn,
  3457.         lParam);
  3458.     FreeProcInstance(fn);
  3459.  
  3460.     return result;
  3461. }
  3462.  
  3463.  
  3464. //
  3465. // GetFreeDiskSpace: Function to Measure Available Disk Space
  3466. //
  3467. static long GetFreeDiskSpaceInKB(LPSTR pFile)
  3468. {
  3469.     DWORD dwFreeClusters, dwBytesPerSector, dwSectorsPerCluster, dwClusters;
  3470.     char RootName[MAX_PATH];
  3471.     LPSTR ptmp;    //required arg
  3472.     ULARGE_INTEGER ulA, ulB, ulFreeBytes;
  3473.  
  3474.     // need to find path for root directory on drive containing
  3475.     // this file.
  3476.  
  3477.     GetFullPathName(pFile, sizeof(RootName), RootName, &ptmp);
  3478.  
  3479.     // truncate this to the name of the root directory (god how tedious)
  3480.     if (RootName[0] == '\\' && RootName[1] == '\\') {
  3481.  
  3482.     // path begins with  \\server\share\path so skip the first
  3483.     // three backslashes
  3484.     ptmp = &RootName[2];
  3485.     while (*ptmp && (*ptmp != '\\')) {
  3486.         ptmp++;
  3487.     }
  3488.     if (*ptmp) {
  3489.         // advance past the third backslash
  3490.         ptmp++;
  3491.     }
  3492.     } else {
  3493.     // path must be drv:\path
  3494.     ptmp = RootName;
  3495.     }
  3496.  
  3497.     // find next backslash and put a null after it
  3498.     while (*ptmp && (*ptmp != '\\')) {
  3499.     ptmp++;
  3500.     }
  3501.     // found a backslash ?
  3502.     if (*ptmp) {
  3503.     // skip it and insert null
  3504.     ptmp++;
  3505.     *ptmp = '\0';
  3506.     }
  3507.  
  3508.     // the only real way of finding out free disk space is calling
  3509.     // GetDiskFreeSpaceExA, but it doesn't exist on Win95
  3510.  
  3511.     HINSTANCE h = LoadLibrary(TEXT("kernel32.dll"));
  3512.     if (h) {
  3513.     typedef BOOL (WINAPI *MyFunc)(LPCSTR RootName, PULARGE_INTEGER pulA, PULARGE_INTEGER pulB, PULARGE_INTEGER pulFreeBytes);
  3514.     MyFunc pfnGetDiskFreeSpaceEx = (MyFunc)GetProcAddress(h,
  3515.                         "GetDiskFreeSpaceExA");
  3516.     FreeLibrary(h);
  3517.     if (pfnGetDiskFreeSpaceEx) {
  3518.         if (!pfnGetDiskFreeSpaceEx(RootName, &ulA, &ulB, &ulFreeBytes))
  3519.         return -1;
  3520.         return (long)(ulFreeBytes.QuadPart / 1024);
  3521.     }
  3522.     }
  3523.  
  3524.     if (!GetDiskFreeSpace(RootName, &dwSectorsPerCluster, &dwBytesPerSector,
  3525.                     &dwFreeClusters, &dwClusters))
  3526.     return (-1);
  3527.     return(MulDiv(dwSectorsPerCluster * dwBytesPerSector,
  3528.            dwFreeClusters,
  3529.            1024));
  3530. }
  3531.  
  3532.  
  3533.  
  3534. // AllocCapFileProc: Capture file Space Allocation Dialog Box Procedure
  3535. //
  3536. int FAR PASCAL AllocCapFileProc(HWND hDlg, UINT Message, UINT wParam, LONG lParam)
  3537. {
  3538.     static int      nFreeMBs = 0 ;
  3539.  
  3540.     switch (Message) {
  3541.     case WM_INITDIALOG:
  3542.     {
  3543.         DWORDLONG        dwlFileSize = 0;
  3544.         long             lFreeSpaceInKB;
  3545.  
  3546.         // Get current capture file name and measure its size
  3547.         dwlFileSize = GetSize(gcap.szCaptureFile);
  3548.  
  3549.         // Get free disk space and add current capture file size to that.
  3550.         // Convert the available space to MBs.
  3551.         if ((lFreeSpaceInKB =
  3552.             GetFreeDiskSpaceInKB(gcap.szCaptureFile)) != -1L) {
  3553.         lFreeSpaceInKB += (long)(dwlFileSize / 1024);
  3554.         nFreeMBs = lFreeSpaceInKB / 1024 ;
  3555.         SetDlgItemInt(hDlg, IDD_SetCapFileFree, nFreeMBs, TRUE) ;
  3556.         } else {
  3557.         EnableWindow(GetDlgItem(hDlg, IDD_SetCapFileFree), FALSE);
  3558.         }
  3559.  
  3560.         gcap.wCapFileSize = (WORD) (dwlFileSize / (1024L * 1024L));
  3561.  
  3562.         SetDlgItemInt(hDlg, IDD_SetCapFileSize, gcap.wCapFileSize, TRUE) ;
  3563.         return TRUE ;
  3564.     }
  3565.  
  3566.     case WM_COMMAND :
  3567.         switch (GET_WM_COMMAND_ID(wParam, lParam)) {
  3568.         case IDOK :
  3569.         {
  3570.             int         iCapFileSize ;
  3571.  
  3572.             iCapFileSize = (int) GetDlgItemInt(hDlg, IDD_SetCapFileSize, NULL, TRUE) ;
  3573.             if (iCapFileSize <= 0 || iCapFileSize > nFreeMBs) {
  3574.             // You are asking for more than we have !! Sorry, ...
  3575.             SetDlgItemInt(hDlg, IDD_SetCapFileSize, iCapFileSize, TRUE) ;
  3576.             SetFocus(GetDlgItem(hDlg, IDD_SetCapFileSize)) ;
  3577.             MessageBeep(MB_ICONEXCLAMATION) ;
  3578.             return FALSE ;
  3579.             }
  3580.             gcap.wCapFileSize = (WORD)iCapFileSize ;
  3581.  
  3582.             EndDialog(hDlg, TRUE) ;
  3583.             return TRUE ;
  3584.         }
  3585.  
  3586.         case IDCANCEL :
  3587.             EndDialog(hDlg, FALSE) ;
  3588.             return TRUE ;
  3589.  
  3590.         case IDD_SetCapFileSize:
  3591.         {
  3592.             long l;
  3593.             BOOL bchanged;
  3594.             TCHAR tachBuffer[21];
  3595.  
  3596.             // check that entered size is a valid number
  3597.             GetDlgItemText(hDlg, IDD_SetCapFileSize, tachBuffer,
  3598.                                                         sizeof(tachBuffer));
  3599.             l = atol(tachBuffer);
  3600.             bchanged = FALSE;
  3601.             if (l < 1) {
  3602.             l = 1;
  3603.             bchanged = TRUE;
  3604.             // don't infinite loop if there's < 1 Meg free
  3605.             } else if (l > nFreeMBs && nFreeMBs > 0) {
  3606.             l = nFreeMBs;
  3607.             bchanged = TRUE;
  3608.             } else {
  3609.             // make sure there are no non-digit chars
  3610.             // atol() will ignore trailing non-digit characters
  3611.             int c = 0;
  3612.             while (tachBuffer[c]) {
  3613.                 if (IsCharAlpha(tachBuffer[c]) ||
  3614.                 !IsCharAlphaNumeric(tachBuffer[c])) {
  3615.  
  3616.                 // string contains non-digit chars - reset
  3617.                 l = 1;
  3618.                 bchanged = TRUE;
  3619.                 break;
  3620.                 }
  3621.                 c++;
  3622.             }
  3623.             }
  3624.             if (bchanged) {
  3625.             wsprintf(tachBuffer, TEXT("%ld"), l);
  3626.             SetDlgItemText(hDlg, IDD_SetCapFileSize, tachBuffer);
  3627.             }
  3628.             break;
  3629.         }
  3630.         }
  3631.         break ;
  3632.     }
  3633.  
  3634.     return FALSE ;
  3635. }
  3636.  
  3637.  
  3638. //
  3639. // FrameRateProc: Choose a frame rate
  3640. //
  3641. int FAR PASCAL FrameRateProc(HWND hwnd, UINT msg, UINT wParam, LONG lParam)
  3642. {
  3643.   TCHAR  tach[32];
  3644.   
  3645.   switch (msg) {
  3646.     case WM_INITDIALOG:
  3647.     /* put the current frame rate in the box */
  3648.     sprintf(tach, TEXT("%f"), gcap.FrameRate, tach);
  3649.     SetDlgItemText(hwnd, IDC_FRAMERATE, tach);
  3650.     CheckDlgButton(hwnd, IDC_USEFRAMERATE, gcap.fUseFrameRate);
  3651.     break;
  3652.     
  3653.     case WM_COMMAND:
  3654.     switch(wParam){
  3655.         case IDCANCEL:
  3656.         EndDialog(hwnd, FALSE);
  3657.         break;
  3658.         
  3659.         case IDOK:
  3660.         /* get the new frame rate */
  3661.         GetDlgItemText(hwnd, IDC_FRAMERATE, tach, sizeof(tach));
  3662.         if (atof(tach) <= 0.) {
  3663.             ErrMsg("Invalid frame rate.");
  3664.             break;
  3665.         }
  3666.         gcap.FrameRate = atof(tach);
  3667.         gcap.fUseFrameRate = IsDlgButtonChecked(hwnd, IDC_USEFRAMERATE);
  3668.         EndDialog(hwnd, TRUE);
  3669.         break;
  3670.     }
  3671.     break;
  3672.     
  3673.     default:
  3674.     return FALSE;
  3675.   }
  3676.   return TRUE;
  3677. }
  3678.  
  3679.  
  3680. //
  3681. // TimeLimitProc: Choose a capture time limit
  3682. //
  3683. int FAR PASCAL TimeLimitProc(HWND hwnd, UINT msg, UINT wParam, LONG lParam)
  3684. {
  3685.   TCHAR   tach[32];
  3686.   DWORD   dwTimeLimit;
  3687.   
  3688.   switch (msg) {
  3689.     case WM_INITDIALOG:
  3690.     /* put the current time limit info in the boxes */
  3691.     sprintf(tach, TEXT("%d"), gcap.dwTimeLimit, tach);
  3692.     SetDlgItemText(hwnd, IDC_TIMELIMIT, tach);
  3693.     CheckDlgButton(hwnd, IDC_USETIMELIMIT, gcap.fUseTimeLimit);
  3694.     break;
  3695.     
  3696.     case WM_COMMAND:
  3697.     switch(wParam){
  3698.         case IDCANCEL:
  3699.         EndDialog(hwnd, FALSE);
  3700.         break;
  3701.         
  3702.         case IDOK:
  3703.         /* get the new time limit */
  3704.         dwTimeLimit = GetDlgItemInt(hwnd, IDC_TIMELIMIT, NULL, FALSE);
  3705.         gcap.dwTimeLimit = dwTimeLimit;
  3706.         gcap.fUseTimeLimit = IsDlgButtonChecked(hwnd, IDC_USETIMELIMIT);
  3707.         EndDialog(hwnd, TRUE);
  3708.         break;
  3709.     }
  3710.     break;
  3711.     
  3712.     default:
  3713.     return FALSE;
  3714.   }
  3715.   return TRUE;
  3716. }
  3717.  
  3718.  
  3719. //
  3720. // PressAKeyProc: Press OK to capture
  3721. //
  3722. int FAR PASCAL PressAKeyProc(HWND hwnd, UINT msg, UINT wParam, LONG lParam)
  3723. {
  3724.   TCHAR tach[_MAX_PATH];
  3725.  
  3726.   switch (msg) {
  3727.     case WM_INITDIALOG:
  3728.     /* set the current file name in the box */
  3729.     wsprintf(tach, TEXT("%s"), gcap.szCaptureFile);
  3730.     SetDlgItemText(hwnd, IDC_CAPFILENAME, tach);
  3731.     break;
  3732.     
  3733.     case WM_COMMAND:
  3734.     switch(wParam){
  3735.         case IDCANCEL:
  3736.         EndDialog(hwnd, FALSE);
  3737.         break;
  3738.         
  3739.         case IDOK:
  3740.         EndDialog(hwnd, TRUE);
  3741.         break;
  3742.     }
  3743.     break;
  3744.     
  3745.     default:
  3746.     return FALSE;
  3747.   }
  3748.   return TRUE;
  3749. }
  3750.  
  3751. DWORDLONG GetSize(LPCTSTR tach)
  3752. {
  3753.     HANDLE hFile = CreateFile(tach, GENERIC_READ, FILE_SHARE_READ, 0,
  3754.                 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
  3755.  
  3756.     if (hFile == INVALID_HANDLE_VALUE) {
  3757.     return 0;
  3758.     }
  3759.  
  3760.     DWORD dwSizeHigh;
  3761.     DWORD dwSizeLow = GetFileSize(hFile, &dwSizeHigh);
  3762.  
  3763.     DWORDLONG dwlSize = dwSizeLow + ((DWORDLONG)dwSizeHigh << 32);
  3764.  
  3765.     if (!CloseHandle(hFile)) {
  3766.     dwlSize = 0;
  3767.     }
  3768.  
  3769.     return dwlSize;
  3770. }
  3771.