home *** CD-ROM | disk | FTP | other *** search
/ Windows Game Programming for Dummies (2nd Edition) / WinGamProgFD.iso / pc / DirectX SDK / DXSDK / samples / Multimedia / DirectShow / Editing / StillCap / StillCapDlg.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2001-10-08  |  29.5 KB  |  1,062 lines

  1. //------------------------------------------------------------------------------
  2. // File: StillCapDlg.cpp
  3. //
  4. // Desc: DirectShow sample code - implementation of callback and dialog
  5. //       objects for StillCap application.
  6. //
  7. // Copyright (c) 1999-2001 Microsoft Corporation.  All rights reserved.
  8. //------------------------------------------------------------------------------
  9.  
  10.  
  11. #include "stdafx.h"
  12. #include "StillCap.h"
  13. #include "StillCapDlg.h"
  14. #include "..\..\common\dshowutil.cpp"
  15.  
  16. #ifdef _DEBUG
  17. #define new DEBUG_NEW
  18. #undef THIS_FILE
  19. static char THIS_FILE[] = __FILE__;
  20. #endif
  21.  
  22. // An application can advertise the existence of its filter graph
  23. // by registering the graph with a global Running Object Table (ROT).
  24. // The GraphEdit application can detect and remotely view the running
  25. // filter graph, allowing you to 'spy' on the graph with GraphEdit.
  26. //
  27. // To enable registration in this sample, define REGISTER_FILTERGRAPH.
  28. //
  29. #ifdef DEBUG
  30. #define REGISTER_FILTERGRAPH
  31. #endif
  32.  
  33. // Constants
  34. #define WM_CAPTURE_BITMAP   WM_APP + 1
  35.  
  36. // Global data
  37. BOOL g_bOneShot = FALSE;
  38. DWORD g_dwGraphRegister=0;  // For running object table
  39. HWND g_hwnd;
  40.  
  41. // Structures
  42. typedef struct _callbackinfo 
  43. {
  44.     double dblSampleTime;
  45.     long lBufferSize;
  46.     BYTE *pBuffer;
  47.     BITMAPINFOHEADER bih;
  48.  
  49. } CALLBACKINFO;
  50.  
  51. CALLBACKINFO cb={0};
  52.  
  53.  
  54. // Note: this object is a SEMI-COM object, and can only be created statically.
  55. // We use this little semi-com object to handle the sample-grab-callback,
  56. // since the callback must provide a COM interface. We could have had an interface
  57. // where you provided a function-call callback, but that's really messy, so we
  58. // did it this way. You can put anything you want into this C++ object, even
  59. // a pointer to a CDialog. Be aware of multi-thread issues though.
  60. //
  61. class CSampleGrabberCB : public ISampleGrabberCB 
  62. {
  63. public:
  64.     // these will get set by the main thread below. We need to
  65.     // know this in order to write out the bmp
  66.     long lWidth;
  67.     long lHeight;
  68.     CStillCapDlg * pOwner;
  69.     TCHAR m_szCapDir[MAX_PATH]; // the directory we want to capture to
  70.     TCHAR m_szSnappedName[MAX_PATH];
  71.     BOOL bFileWritten;
  72.  
  73.     CSampleGrabberCB( )
  74.     {
  75.         pOwner = NULL;
  76.         m_szCapDir[0] = 0;
  77.         bFileWritten = FALSE;
  78.     }   
  79.  
  80.     // fake out any COM ref counting
  81.     //
  82.     STDMETHODIMP_(ULONG) AddRef() { return 2; }
  83.     STDMETHODIMP_(ULONG) Release() { return 1; }
  84.  
  85.     // fake out any COM QI'ing
  86.     //
  87.     STDMETHODIMP QueryInterface(REFIID riid, void ** ppv)
  88.     {
  89.         if( riid == IID_ISampleGrabberCB || riid == IID_IUnknown ) 
  90.         {
  91.             *ppv = (void *) static_cast<ISampleGrabberCB*> ( this );
  92.             return NOERROR;
  93.         }    
  94.         return E_NOINTERFACE;
  95.     }
  96.  
  97.     // we don't implement this interface for this example
  98.     //
  99.     STDMETHODIMP SampleCB( double SampleTime, IMediaSample * pSample )
  100.     {
  101.         return 0;
  102.     }
  103.  
  104.     // The sample grabber is calling us back on its deliver thread.
  105.     // This is NOT the main app thread!
  106.     //
  107.     //           !!!!! WARNING WARNING WARNING !!!!!
  108.     //
  109.     // On Windows 9x systems, you are not allowed to call most of the 
  110.     // Windows API functions in this callback.  Why not?  Because the
  111.     // video renderer might hold the global Win16 lock so that the video
  112.     // surface can be locked while you copy its data.  This is not an
  113.     // issue on Windows 2000, but is a limitation on Win95,98,98SE, and ME.
  114.     // Calling a 16-bit legacy function could lock the system, because 
  115.     // it would wait forever for the Win16 lock, which would be forever
  116.     // held by the video renderer.
  117.     //
  118.     // As a workaround, copy the bitmap data during the callback,
  119.     // post a message to our app, and write the data later.
  120.     //
  121.     STDMETHODIMP BufferCB( double dblSampleTime, BYTE * pBuffer, long lBufferSize )
  122.     {
  123.         // this flag will get set to true in order to take a picture
  124.         //
  125.         if( !g_bOneShot )
  126.             return 0;
  127.  
  128.         // Since we can't access Windows API functions in this callback, just
  129.         // copy the bitmap data to a global structure for later reference.
  130.         cb.dblSampleTime = dblSampleTime;
  131.         cb.lBufferSize   = lBufferSize;
  132.  
  133.         // If we haven't yet allocated the data buffer, do it now.
  134.         // Just allocate what we need to store the new bitmap.
  135.         if (!cb.pBuffer)
  136.             cb.pBuffer = new BYTE[lBufferSize];
  137.  
  138.         // Copy the bitmap data into our global buffer
  139.         if (cb.pBuffer)
  140.             memcpy(cb.pBuffer, pBuffer, lBufferSize);
  141.  
  142.         // Post a message to our application, telling it to come back
  143.         // and write the saved data to a bitmap file on the user's disk.
  144.         PostMessage(g_hwnd, WM_CAPTURE_BITMAP, 0, 0L);
  145.         return 0;
  146.     }
  147.  
  148.     // This function will be called whenever a captured still needs to be
  149.     // displayed in the preview window.  It is called initially within
  150.     // CopyBitmap() to display the captured still, but it is also called
  151.     // whenever the main dialog needs to repaint and when we transition
  152.     // from video capture mode back into still capture mode.
  153.     //
  154.     BOOL DisplayCapturedBits(BYTE *pBuffer, BITMAPINFOHEADER *pbih)
  155.     {
  156.         // If we haven't yet snapped a still, return
  157.         if (!bFileWritten || !pOwner || !pBuffer)
  158.             return FALSE;
  159.  
  160.         // put bits into the preview window with StretchDIBits
  161.         //
  162.         HWND hwndStill = NULL;
  163.         pOwner->GetDlgItem( IDC_STILL, &hwndStill );
  164.  
  165.         RECT rc;
  166.         ::GetWindowRect( hwndStill, &rc );
  167.         long lStillWidth = rc.right - rc.left;
  168.         long lStillHeight = rc.bottom - rc.top;
  169.         
  170.         HDC hdcStill = GetDC( hwndStill );
  171.         PAINTSTRUCT ps;
  172.         BeginPaint(hwndStill, &ps);
  173.  
  174.         SetStretchBltMode(hdcStill, COLORONCOLOR);
  175.         StretchDIBits( 
  176.             hdcStill, 0, 0, 
  177.             lStillWidth, lStillHeight, 
  178.             0, 0, lWidth, lHeight, 
  179.             pBuffer, 
  180.             (BITMAPINFO*) pbih, 
  181.             DIB_RGB_COLORS, 
  182.             SRCCOPY );
  183.  
  184.         EndPaint(hwndStill, &ps);
  185.         ReleaseDC( hwndStill, hdcStill );    
  186.  
  187.         return TRUE;
  188.     }
  189.  
  190.     // This is the implementation function that writes the captured video
  191.     // data onto a bitmap on the user's disk.
  192.     //
  193.     BOOL CopyBitmap( double dblSampleTime, BYTE * pBuffer, long lBufferSize )
  194.     {
  195.         if( !g_bOneShot )
  196.             return 0;
  197.  
  198.         // we only take one at a time
  199.         //
  200.         g_bOneShot = FALSE;
  201.  
  202.         // figure out where to capture to
  203.         //
  204.         TCHAR m_ShortName[MAX_PATH];
  205.         wsprintf( m_szSnappedName, TEXT("%sStillCap%4.4ld.bmp"), 
  206.                   m_szCapDir, pOwner->m_nCapTimes );
  207.         wsprintf( m_ShortName, TEXT("StillCap%4.4ld.bmp"), 
  208.                   pOwner->m_nCapTimes );
  209.  
  210.         // increment bitmap number if user requested it
  211.         // otherwise, we'll reuse the filename next time
  212.         if( pOwner->IsDlgButtonChecked( IDC_AUTOBUMP ) )
  213.             pOwner->m_nCapTimes++;
  214.  
  215.         // write out a BMP file
  216.         //
  217.         HANDLE hf = CreateFile(
  218.             m_szSnappedName, GENERIC_WRITE, 0, NULL,
  219.             CREATE_ALWAYS, NULL, NULL );
  220.  
  221.         if( hf == INVALID_HANDLE_VALUE )
  222.             return 0;
  223.  
  224.         // write out the file header
  225.         //
  226.         BITMAPFILEHEADER bfh;
  227.         memset( &bfh, 0, sizeof( bfh ) );
  228.         bfh.bfType = 'MB';
  229.         bfh.bfSize = sizeof( bfh ) + lBufferSize + sizeof( BITMAPINFOHEADER );
  230.         bfh.bfOffBits = sizeof( BITMAPINFOHEADER ) + sizeof( BITMAPFILEHEADER );
  231.  
  232.         DWORD dwWritten = 0;
  233.         WriteFile( hf, &bfh, sizeof( bfh ), &dwWritten, NULL );
  234.  
  235.         // and the bitmap format
  236.         //
  237.         BITMAPINFOHEADER bih;
  238.         memset( &bih, 0, sizeof( bih ) );
  239.         bih.biSize = sizeof( bih );
  240.         bih.biWidth = lWidth;
  241.         bih.biHeight = lHeight;
  242.         bih.biPlanes = 1;
  243.         bih.biBitCount = 24;
  244.  
  245.         dwWritten = 0;
  246.         WriteFile( hf, &bih, sizeof( bih ), &dwWritten, NULL );
  247.  
  248.         // and the bits themselves
  249.         //
  250.         dwWritten = 0;
  251.         WriteFile( hf, pBuffer, lBufferSize, &dwWritten, NULL );
  252.         CloseHandle( hf );
  253.         bFileWritten = TRUE;
  254.  
  255.         // Display the bitmap bits on the dialog's preview window
  256.         DisplayCapturedBits(pBuffer, &bih);
  257.  
  258.         // Save bitmap header for later use when repainting the window
  259.         memcpy(&(cb.bih), &bih, sizeof(bih));        
  260.  
  261.         // show where it captured
  262.         //
  263.         pOwner->SetDlgItemText( IDC_SNAPNAME, m_ShortName );
  264.  
  265.         // Enable the 'View Still' button
  266.         HWND hwndButton = NULL;
  267.         pOwner->GetDlgItem( IDC_BUTTON_VIEWSTILL, &hwndButton );
  268.         ::EnableWindow(hwndButton, TRUE);
  269.  
  270.         // play a snap sound
  271.         if (pOwner->IsDlgButtonChecked(IDC_PLAYSOUND))
  272.         {
  273.             TCHAR szSound[128];
  274.             GetWindowsDirectory(szSound, 128);
  275.             _tcscat(szSound, TEXT("\\media\\click.wav\0"));
  276.             sndPlaySound(szSound, SND_ASYNC);
  277.         }
  278.  
  279.         return 0;
  280.     }
  281.  
  282. };
  283.  
  284. // this semi-COM object will receive sample callbacks for us
  285. //
  286. CSampleGrabberCB mCB;
  287.  
  288. /////////////////////////////////////////////////////////////////////////////
  289. // CAboutDlg dialog used for App About
  290.  
  291. class CAboutDlg : public CDialog
  292. {
  293. public:
  294.     CAboutDlg();
  295.  
  296. // Dialog Data
  297.     //{{AFX_DATA(CAboutDlg)
  298.     enum { IDD = IDD_ABOUTBOX };
  299.     //}}AFX_DATA
  300.  
  301.     // ClassWizard generated virtual function overrides
  302.     //{{AFX_VIRTUAL(CAboutDlg)
  303.     protected:
  304.     virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
  305.     //}}AFX_VIRTUAL
  306.  
  307. // Implementation
  308. protected:
  309.     //{{AFX_MSG(CAboutDlg)
  310.     //}}AFX_MSG
  311.     DECLARE_MESSAGE_MAP()
  312. };
  313.  
  314. CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
  315. {
  316.     //{{AFX_DATA_INIT(CAboutDlg)
  317.     //}}AFX_DATA_INIT
  318. }
  319.  
  320. void CAboutDlg::DoDataExchange(CDataExchange* pDX)
  321. {
  322.     CDialog::DoDataExchange(pDX);
  323.     //{{AFX_DATA_MAP(CAboutDlg)
  324.     //}}AFX_DATA_MAP
  325. }
  326.  
  327. BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
  328.     //{{AFX_MSG_MAP(CAboutDlg)
  329.         // No message handlers
  330.     //}}AFX_MSG_MAP
  331. END_MESSAGE_MAP()
  332.  
  333. /////////////////////////////////////////////////////////////////////////////
  334. // CStillCapDlg dialog
  335.  
  336. CStillCapDlg::CStillCapDlg(CWnd* pParent /*=NULL*/)
  337.     : CDialog(CStillCapDlg::IDD, pParent)
  338. {
  339.     //{{AFX_DATA_INIT(CStillCapDlg)
  340.         // NOTE: the ClassWizard will add member initialization here
  341.     //}}AFX_DATA_INIT
  342.     // Note that LoadIcon does not require a subsequent DestroyIcon in Win32
  343.     m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
  344. }
  345.  
  346. void CStillCapDlg::DoDataExchange(CDataExchange* pDX)
  347. {
  348.     CDialog::DoDataExchange(pDX);
  349.     //{{AFX_DATA_MAP(CStillCapDlg)
  350.     DDX_Control(pDX, IDC_STATUS, m_StrStatus);
  351.     DDX_Control(pDX, IDC_STILL, m_StillScreen);
  352.     DDX_Control(pDX, IDC_PREVIEW, m_PreviewScreen);
  353.     //}}AFX_DATA_MAP
  354. }
  355.  
  356. BEGIN_MESSAGE_MAP(CStillCapDlg, CDialog)
  357.     //{{AFX_MSG_MAP(CStillCapDlg)
  358.     ON_WM_PAINT()
  359.     ON_WM_SYSCOMMAND()
  360.     ON_WM_QUERYDRAGICON()
  361.     ON_BN_CLICKED(IDC_SNAP, OnSnap)
  362.     ON_BN_CLICKED(IDC_CAPSTILLS, OnCapstills)
  363.     ON_BN_CLICKED(IDC_CAPVID, OnCapvid)
  364.     ON_BN_CLICKED(IDC_BUTTON_RESET, OnButtonReset)
  365.     ON_BN_CLICKED(IDC_BUTTON_VIEWSTILL, OnButtonViewstill)
  366.     ON_WM_CLOSE()
  367.     //}}AFX_MSG_MAP
  368. END_MESSAGE_MAP()
  369.  
  370. /////////////////////////////////////////////////////////////////////////////
  371. // CStillCapDlg message handlers
  372.  
  373. void CStillCapDlg::OnSysCommand(UINT nID, LPARAM lParam)
  374. {
  375.     if ((nID & 0xFFF0) == IDM_ABOUTBOX)
  376.     {
  377.         CAboutDlg dlgAbout;
  378.         dlgAbout.DoModal();
  379.     }
  380.     else
  381.     {
  382.         CDialog::OnSysCommand(nID, lParam);
  383.     }
  384. }
  385.  
  386.  
  387. // If you add a minimize button to your dialog, you will need the code below
  388. //  to draw the icon.  For MFC applications using the document/view model,
  389. //  this is automatically done for you by the framework.
  390. void CStillCapDlg::OnPaint() 
  391. {
  392.     if (IsIconic())
  393.     {
  394.         CPaintDC dc(this); // device context for painting
  395.  
  396.         SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);
  397.  
  398.         // Center icon in client rectangle
  399.         int cxIcon = GetSystemMetrics(SM_CXICON);
  400.         int cyIcon = GetSystemMetrics(SM_CYICON);
  401.         CRect rect;
  402.         GetClientRect(&rect);
  403.         int x = (rect.Width() - cxIcon + 1) / 2;
  404.         int y = (rect.Height() - cyIcon + 1) / 2;
  405.  
  406.         // Draw the icon
  407.         dc.DrawIcon(x, y, m_hIcon);
  408.     }
  409.     else
  410.     {
  411.         CDialog::OnPaint();
  412.  
  413.         // Update the bitmap preview window, if we have
  414.         // already captured bitmap data
  415.         mCB.DisplayCapturedBits(cb.pBuffer, &(cb.bih));
  416.     }
  417. }
  418.  
  419. // The system calls this to obtain the cursor to display while the user drags
  420. //  the minimized window.
  421. HCURSOR CStillCapDlg::OnQueryDragIcon()
  422. {
  423.     return (HCURSOR) m_hIcon;
  424. }
  425.  
  426. BOOL CStillCapDlg::OnInitDialog()
  427. {
  428.     CDialog::OnInitDialog();
  429.  
  430.     // Add "About..." menu item to system menu.
  431.  
  432.     // IDM_ABOUTBOX must be in the system command range.
  433.     ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
  434.     ASSERT(IDM_ABOUTBOX < 0xF000);
  435.  
  436.     CMenu* pSysMenu = GetSystemMenu(FALSE);
  437.     if (pSysMenu != NULL)
  438.     {
  439.         CString strAboutMenu;
  440.         strAboutMenu.LoadString(IDS_ABOUTBOX);
  441.         if (!strAboutMenu.IsEmpty())
  442.         {
  443.             pSysMenu->AppendMenu(MF_SEPARATOR);
  444.             pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
  445.         }
  446.     }
  447.  
  448.     // Set the icon for this dialog.  The framework does this automatically
  449.     //  when the application's main window is not a dialog
  450.     SetIcon(m_hIcon, TRUE);            // Set big icon
  451.     SetIcon(m_hIcon, FALSE);        // Set small icon
  452.     
  453.     // StillCap-specific initialization
  454.     CoInitialize( NULL );
  455.  
  456.     // default to this capture directory
  457.     //
  458.     SetDlgItemText( IDC_CAPDIR, TEXT("c:\\") );
  459.  
  460.     // default to capturing stills
  461.     //
  462.     CheckDlgButton( IDC_CAPSTILLS, 1 );
  463.     m_bCapStills = true;
  464.     m_nCapState = 0;
  465.     m_nCapTimes = 0;
  466.     g_hwnd = GetSafeHwnd();
  467.  
  468.     // start up the still image capture graph
  469.     //
  470.     HRESULT hr = InitStillGraph( );
  471.     if (FAILED(hr))
  472.         Error( TEXT("Failed to initialize StillGraph!"));
  473.  
  474.     // Modify the window style of the capture and still windows
  475.     // to prevent excessive repainting
  476.     m_PreviewScreen.ModifyStyle(0, WS_CLIPCHILDREN);
  477.     m_StillScreen.ModifyStyle(0, WS_CLIPCHILDREN);
  478.  
  479.     return TRUE;  // return TRUE  unless you set the focus to a control
  480. }
  481.  
  482. void CStillCapDlg::ClearGraphs( )
  483. {
  484.     // Destroy capture graph
  485.     if( m_pGraph )
  486.     {
  487.         // have to wait for the graphs to stop first
  488.         //
  489.         CComQIPtr< IMediaControl, &IID_IMediaControl > pControl = m_pGraph;
  490.         if( pControl ) 
  491.             pControl->Stop( );
  492.  
  493.         // make the window go away before we release graph
  494.         // or we'll leak memory/resources
  495.         // 
  496.         CComQIPtr< IVideoWindow, &IID_IVideoWindow > pWindow = m_pGraph;
  497.         if( pWindow )
  498.         {
  499.             pWindow->put_Visible( OAFALSE );
  500.             pWindow->put_Owner( NULL );
  501.         }
  502.  
  503. #ifdef REGISTER_FILTERGRAPH
  504.         // Remove filter graph from the running object table   
  505.         if (g_dwGraphRegister)
  506.             RemoveGraphFromRot(g_dwGraphRegister);
  507. #endif
  508.  
  509.         m_pGraph.Release( );
  510.         m_pGrabber.Release( );
  511.     }
  512.  
  513.     // Destroy playback graph, if it exists
  514.     if( m_pPlayGraph )
  515.     {
  516.         CComQIPtr< IMediaControl, &IID_IMediaControl > pControl = m_pPlayGraph;
  517.         if( pControl ) 
  518.             pControl->Stop( );
  519.  
  520.         CComQIPtr< IVideoWindow, &IID_IVideoWindow > pWindow = m_pPlayGraph;
  521.         if( pWindow )
  522.         {
  523.             pWindow->put_Visible( OAFALSE );
  524.             pWindow->put_Owner( NULL );
  525.         }
  526.  
  527.         m_pPlayGraph.Release( );
  528.     }
  529. }
  530.  
  531. HRESULT CStillCapDlg::InitStillGraph( )
  532. {
  533.     HRESULT hr;
  534.  
  535.     // create a filter graph
  536.     //
  537.     hr = m_pGraph.CoCreateInstance( CLSID_FilterGraph );
  538.     if( !m_pGraph )
  539.     {
  540.         Error( TEXT("Could not create filter graph") );
  541.         return E_FAIL;
  542.     }
  543.  
  544.     // get whatever capture device exists
  545.     //
  546.     CComPtr< IBaseFilter > pCap;
  547.     GetDefaultCapDevice( &pCap );
  548.     if( !pCap )
  549.     {
  550.         Error( TEXT("No video capture device was detected on your system.\r\n\r\n")
  551.                TEXT("This sample requires a functional video capture device, such\r\n")
  552.                TEXT("as a USB web camera.") );
  553.         return E_FAIL;
  554.     }
  555.  
  556.     // add the capture filter to the graph
  557.     //
  558.     hr = m_pGraph->AddFilter( pCap, L"Cap" );
  559.     if( FAILED( hr ) )
  560.     {
  561.         Error( TEXT("Could not put capture device in graph"));
  562.         return E_FAIL;
  563.     }
  564.  
  565.     // create a sample grabber
  566.     //
  567.     hr = m_pGrabber.CoCreateInstance( CLSID_SampleGrabber );
  568.     if( !m_pGrabber )
  569.     {
  570.         Error( TEXT("Could not create SampleGrabber (is qedit.dll registered?)"));
  571.         return hr;
  572.     }
  573.     CComQIPtr< IBaseFilter, &IID_IBaseFilter > pGrabBase( m_pGrabber );
  574.  
  575.     // force it to connect to video, 24 bit
  576.     //
  577.     CMediaType VideoType;
  578.     VideoType.SetType( &MEDIATYPE_Video );
  579.     VideoType.SetSubtype( &MEDIASUBTYPE_RGB24 );
  580.     hr = m_pGrabber->SetMediaType( &VideoType ); // shouldn't fail
  581.     if( FAILED( hr ) )
  582.     {
  583.         Error( TEXT("Could not set media type"));
  584.         return hr;
  585.     }
  586.  
  587.     // add the grabber to the graph
  588.     //
  589.     hr = m_pGraph->AddFilter( pGrabBase, L"Grabber" );
  590.     if( FAILED( hr ) )
  591.     {
  592.         Error( TEXT("Could not put sample grabber in graph"));
  593.         return hr;
  594.     }
  595.  
  596.     // find the two pins and connect them
  597.     //
  598.     IPin * pCapOut = GetOutPin( pCap, 0 );
  599.     IPin * pGrabIn = GetInPin( pGrabBase, 0 );
  600.     hr = m_pGraph->Connect( pCapOut, pGrabIn );
  601.     if( FAILED( hr ) )
  602.     {
  603.         Error( TEXT("Could not connect capture pin #0 to grabber.\r\n")
  604.                TEXT("Is the capture device being used by another application?"));
  605.         return hr;
  606.     }
  607.  
  608.     // render the sample grabber output pin, so we get a preview window
  609.     //
  610.     IPin * pGrabOut = GetOutPin( pGrabBase, 0 );
  611.     hr = m_pGraph->Render( pGrabOut );
  612.     if( FAILED( hr ) )
  613.     {
  614.         Error( TEXT("Could not render sample grabber output pin"));
  615.         return hr;
  616.     }
  617.  
  618.     // ask for the connection media type so we know how big
  619.     // it is, so we can write out bitmaps
  620.     //
  621.     AM_MEDIA_TYPE mt;
  622.     hr = m_pGrabber->GetConnectedMediaType( &mt );
  623.     if ( FAILED( hr) )
  624.     {
  625.         Error( TEXT("Could not read the connected media type"));
  626.         return hr;
  627.     }
  628.     
  629.     VIDEOINFOHEADER * vih = (VIDEOINFOHEADER*) mt.pbFormat;
  630.     mCB.pOwner = this;
  631.     mCB.lWidth  = vih->bmiHeader.biWidth;
  632.     mCB.lHeight = vih->bmiHeader.biHeight;
  633.     FreeMediaType( mt );
  634.  
  635.     // don't buffer the samples as they pass through
  636.     //
  637.     m_pGrabber->SetBufferSamples( FALSE );
  638.  
  639.     // only grab one at a time, stop stream after
  640.     // grabbing one sample
  641.     //
  642.     m_pGrabber->SetOneShot( FALSE );
  643.  
  644.     // set the callback, so we can grab the one sample
  645.     //
  646.     m_pGrabber->SetCallback( &mCB, 1 );
  647.  
  648.     // find the video window and stuff it in our window
  649.     //
  650.     CComQIPtr< IVideoWindow, &IID_IVideoWindow > pWindow = m_pGraph;
  651.     if( !pWindow )
  652.     {
  653.         Error( TEXT("Could not get video window interface"));
  654.         return E_FAIL;
  655.     }
  656.  
  657.     // set up the preview window to be in our dialog
  658.     // instead of floating popup
  659.     //
  660.     HWND hwndPreview = NULL;
  661.     GetDlgItem( IDC_PREVIEW, &hwndPreview );
  662.     RECT rc;
  663.     ::GetWindowRect( hwndPreview, &rc );
  664.     pWindow->put_Owner( (OAHWND) hwndPreview );
  665.     pWindow->put_Left( 0 );
  666.     pWindow->put_Top( 0 );
  667.     pWindow->put_Width( rc.right - rc.left );
  668.     pWindow->put_Height( rc.bottom - rc.top );
  669.     pWindow->put_Visible( OATRUE );
  670.     pWindow->put_WindowStyle( WS_CHILD | WS_CLIPSIBLINGS );
  671.     
  672.     // Add our graph to the running object table, which will allow
  673.     // the GraphEdit application to "spy" on our graph
  674. #ifdef REGISTER_FILTERGRAPH
  675.     hr = AddGraphToRot(m_pGraph, &g_dwGraphRegister);
  676.     if (FAILED(hr))
  677.     {
  678.         Error(TEXT("Failed to register filter graph with ROT!"));
  679.         g_dwGraphRegister = 0;
  680.     }
  681. #endif
  682.  
  683.     // run the graph
  684.     //
  685.     CComQIPtr< IMediaControl, &IID_IMediaControl > pControl = m_pGraph;
  686.     hr = pControl->Run( );
  687.     if( FAILED( hr ) )
  688.     {
  689.         Error( TEXT("Could not run graph"));
  690.         return hr;
  691.     }
  692.  
  693.     UpdateStatus(_T("Previewing Live Video"));
  694.     return 0;
  695. }
  696.  
  697. HRESULT CStillCapDlg::InitCaptureGraph( TCHAR * pFilename )
  698. {
  699.     HRESULT hr;
  700.  
  701.     // make a filter graph
  702.     //
  703.     m_pGraph.CoCreateInstance( CLSID_FilterGraph );
  704.     if( !m_pGraph )
  705.     {
  706.         Error(TEXT("Could not create filter graph"));
  707.         return E_FAIL;
  708.     }
  709.  
  710.     // get whatever capture device exists
  711.     //
  712.     CComPtr< IBaseFilter > pCap;
  713.     GetDefaultCapDevice( &pCap );
  714.     if( !pCap )
  715.     {
  716.         Error( TEXT("No video capture device was detected on your system.\r\n\r\n")
  717.                TEXT("This sample requires a functional video capture device, such\r\n")
  718.                TEXT("as a USB web camera.") );
  719.         return E_FAIL;
  720.     }
  721.  
  722.     // add the capture filter to the graph
  723.     //
  724.     hr = m_pGraph->AddFilter( pCap, L"Cap" );
  725.     if( FAILED( hr ) )
  726.     {
  727.         Error( TEXT("Could not put capture device in graph"));
  728.         return hr;
  729.     }
  730.  
  731.     // make a capture builder graph (for connecting help)
  732.     //
  733.     CComPtr< ICaptureGraphBuilder2 > pBuilder;
  734.     hr = pBuilder.CoCreateInstance( CLSID_CaptureGraphBuilder2 );
  735.     if( !pBuilder )
  736.     {
  737.         Error( TEXT("Could not create capture graph builder2"));
  738.         return hr;
  739.     }
  740.  
  741.     hr = pBuilder->SetFiltergraph( m_pGraph );
  742.     if( FAILED( hr ) )
  743.     {
  744.         Error( TEXT("Could not set filtergraph on graphbuilder2"));
  745.         return hr;
  746.     }
  747.  
  748.     CComPtr< IBaseFilter > pMux;
  749.     CComPtr< IFileSinkFilter > pSink;
  750.     USES_CONVERSION;
  751.  
  752.     hr = pBuilder->SetOutputFileName( &MEDIASUBTYPE_Avi,
  753.         T2W( pFilename ),
  754.         &pMux,
  755.         &pSink );
  756.     if( FAILED( hr ) )
  757.     {
  758.         Error( TEXT("Could not create/hookup mux and writer"));
  759.         return hr;
  760.     }
  761.  
  762.     hr = pBuilder->RenderStream( &PIN_CATEGORY_CAPTURE,
  763.         &MEDIATYPE_Video,
  764.         pCap,
  765.         NULL,
  766.         pMux );
  767.     if( FAILED( hr ) )
  768.     {
  769.         Error( TEXT("Could not connect capture pin"));
  770.         return hr;
  771.     }
  772.  
  773.     hr = pBuilder->RenderStream( &PIN_CATEGORY_PREVIEW,
  774.         &MEDIATYPE_Video,
  775.         pCap,
  776.         NULL,
  777.         NULL );
  778.     if( FAILED( hr ) )
  779.     {
  780.         Error( TEXT("Could not render capture pin"));
  781.         return hr;
  782.     }
  783.     if( hr == VFW_S_NOPREVIEWPIN )
  784.     {
  785.         // preview was faked up using the capture pin, so we can't
  786.         // turn capture on and off at will.
  787.         hr = 0;
  788.     }
  789.  
  790.     // find the video window and stuff it in our window
  791.     //
  792.     CComQIPtr< IVideoWindow, &IID_IVideoWindow > pWindow = m_pGraph;
  793.     if( !pWindow )
  794.     {
  795.         Error( TEXT("Could not get video window interface"));
  796.         return hr;
  797.     }
  798.  
  799.     // set up the preview window to be in our dialog
  800.     // instead of floating popup
  801.     //
  802.     HWND hwndPreview = NULL;
  803.     GetDlgItem( IDC_PREVIEW, &hwndPreview );
  804.     RECT rc;
  805.     ::GetWindowRect( hwndPreview, &rc );
  806.     pWindow->put_Owner( (OAHWND) hwndPreview );
  807.     pWindow->put_Left( 0 );
  808.     pWindow->put_Top( 0 );
  809.     pWindow->put_Width( rc.right - rc.left );
  810.     pWindow->put_Height( rc.bottom - rc.top );
  811.     pWindow->put_Visible( OATRUE );
  812.     pWindow->put_WindowStyle( WS_CHILD | WS_CLIPSIBLINGS );
  813.     
  814.     // run the graph
  815.     //
  816.     CComQIPtr< IMediaControl, &IID_IMediaControl > pControl = m_pGraph;
  817.     hr = pControl->Run( );
  818.     if( FAILED( hr ) )
  819.     {
  820.         Error( TEXT("Could not run graph"));
  821.         return hr;
  822.     }
  823.  
  824.     UpdateStatus(_T("Capturing Video To Disk"));
  825.     return 0;
  826. }
  827.  
  828. HRESULT CStillCapDlg::InitPlaybackGraph( TCHAR * pFilename )
  829. {
  830.     m_pPlayGraph.CoCreateInstance( CLSID_FilterGraph );
  831.     USES_CONVERSION;
  832.  
  833.     HRESULT hr = m_pPlayGraph->RenderFile( T2W( pFilename ), NULL );
  834.     if (FAILED(hr))
  835.         return hr;
  836.  
  837.     // find the video window and stuff it in our window
  838.     //
  839.     CComQIPtr< IVideoWindow, &IID_IVideoWindow > pWindow = m_pPlayGraph;
  840.     if( !pWindow )
  841.     {
  842.         Error( TEXT("Could not get video window interface"));
  843.         return E_FAIL;
  844.     }
  845.  
  846.     // set up the preview window to be in our dialog
  847.     // instead of floating popup
  848.     //
  849.     HWND hwndPreview = NULL;
  850.     GetDlgItem( IDC_STILL, &hwndPreview );
  851.     RECT rc;
  852.     ::GetWindowRect( hwndPreview, &rc );
  853.     pWindow->put_Owner( (OAHWND) hwndPreview );
  854.     pWindow->put_Left( 0 );
  855.     pWindow->put_Top( 0 );
  856.     pWindow->put_Width( rc.right - rc.left );
  857.     pWindow->put_Height( rc.bottom - rc.top );
  858.     pWindow->put_Visible( OATRUE );
  859.     pWindow->put_WindowStyle( WS_CHILD | WS_CLIPSIBLINGS );
  860.  
  861.     CComQIPtr< IMediaControl, &IID_IMediaControl > pControl;
  862.     pControl = m_pPlayGraph;
  863.  
  864.     // Play back the recorded video
  865.     pControl->Run( );
  866.     UpdateStatus(_T("Playing Back Recorded Video"));
  867.     return 0;
  868. }
  869.  
  870. void CStillCapDlg::GetDefaultCapDevice( IBaseFilter ** ppCap )
  871. {
  872.     HRESULT hr;
  873.  
  874.     *ppCap = NULL;
  875.  
  876.     // create an enumerator
  877.     //
  878.     CComPtr< ICreateDevEnum > pCreateDevEnum;
  879.     pCreateDevEnum.CoCreateInstance( CLSID_SystemDeviceEnum );
  880.     if( !pCreateDevEnum )
  881.         return;
  882.  
  883.     // enumerate video capture devices
  884.     //
  885.     CComPtr< IEnumMoniker > pEm;
  886.     pCreateDevEnum->CreateClassEnumerator( CLSID_VideoInputDeviceCategory, &pEm, 0 );
  887.     if( !pEm )
  888.         return;
  889.  
  890.     pEm->Reset( );
  891.  
  892.     // go through and find first video capture device
  893.     //
  894.     while( 1 )
  895.     {
  896.         ULONG ulFetched = 0;
  897.         CComPtr< IMoniker > pM;
  898.         hr = pEm->Next( 1, &pM, &ulFetched );
  899.         if( hr != S_OK )
  900.             break;
  901.  
  902.         // get the property bag interface from the moniker
  903.         //
  904.         CComPtr< IPropertyBag > pBag;
  905.         hr = pM->BindToStorage( 0, 0, IID_IPropertyBag, (void**) &pBag );
  906.         if( hr != S_OK )
  907.             continue;
  908.  
  909.         // ask for the english-readable name
  910.         //
  911.         CComVariant var;
  912.         var.vt = VT_BSTR;
  913.         hr = pBag->Read( L"FriendlyName", &var, NULL );
  914.         if( hr != S_OK )
  915.             continue;
  916.  
  917.         // set it in our UI
  918.         //
  919.         USES_CONVERSION;
  920.         SetDlgItemText( IDC_CAPOBJ, W2T( var.bstrVal ) );
  921.  
  922.         // ask for the actual filter
  923.         //
  924.         hr = pM->BindToObject( 0, 0, IID_IBaseFilter, (void**) ppCap );
  925.         if( *ppCap )
  926.             break;
  927.     }
  928.  
  929.     return;
  930. }
  931.  
  932. void CStillCapDlg::OnSnap() 
  933. {
  934.     CString CapDir;
  935.     GetDlgItemText( IDC_CAPDIR, CapDir );
  936.  
  937.     // Snap a still picture?
  938.     if( m_bCapStills )
  939.     {
  940.         _tcscpy( mCB.m_szCapDir, CapDir );
  941.         g_bOneShot = TRUE;
  942.     }
  943.  
  944.     // Start capturing video
  945.     else
  946.     {
  947.         if( m_nCapState == 0 )
  948.         {
  949.             if( IsDlgButtonChecked( IDC_AUTOBUMP ) )
  950.                 m_nCapTimes++;
  951.         }
  952.  
  953.         // Determine AVI filename
  954.         TCHAR szFilename[MAX_PATH], szFile[MAX_PATH];
  955.         wsprintf( szFilename, TEXT("%sStillCap%04d.avi"), CapDir, m_nCapTimes );
  956.         wsprintf( szFile, TEXT("StillCap%04d.avi"), m_nCapTimes );
  957.  
  958.         // start capturing, show playing button
  959.         //
  960.         if( m_nCapState == 0 )
  961.         {
  962.             ClearGraphs( );
  963.             InitCaptureGraph( szFilename );
  964.             SetDlgItemText( IDC_SNAP, TEXT("&Start Playback"));
  965.             m_nCapState = 1;
  966.         }
  967.         else if( m_nCapState == 1 )
  968.         {
  969.             // show us where it captured to
  970.             //
  971.             SetDlgItemText( IDC_SNAPNAME, szFile );
  972.  
  973.             ClearGraphs( );
  974.             InitPlaybackGraph( szFilename );
  975.             SetDlgItemText( IDC_SNAP, TEXT("&Start Capture"));
  976.             m_nCapState = 0;
  977.         }
  978.     }
  979. }
  980.  
  981. BOOL CStillCapDlg::DestroyWindow() 
  982. {
  983.     ClearGraphs( );
  984.  
  985.     return CDialog::DestroyWindow();
  986. }
  987.  
  988. void CStillCapDlg::Error( TCHAR * pText )
  989. {
  990.     GetDlgItem( IDC_SNAP )->EnableWindow( FALSE );
  991.     ::MessageBox( NULL, pText, TEXT("Error!"), MB_OK | MB_TASKMODAL | MB_SETFOREGROUND );
  992. }
  993.  
  994. void CStillCapDlg::OnCapstills() 
  995. {
  996.     if( m_bCapStills )
  997.         return;
  998.  
  999.     SetDlgItemText( IDC_SNAP, TEXT("&Snap Still"));
  1000.     m_bCapStills = true;
  1001.  
  1002.     ClearGraphs( );
  1003.     InitStillGraph( );
  1004.  
  1005.     // Update the bitmap preview window, if we have
  1006.     // already captured bitmap data
  1007.     mCB.DisplayCapturedBits(cb.pBuffer, &(cb.bih));
  1008. }
  1009.  
  1010. void CStillCapDlg::OnCapvid() 
  1011. {
  1012.     if( !m_bCapStills )
  1013.         return;
  1014.  
  1015.     ClearGraphs( );
  1016.     m_bCapStills = false;
  1017.     m_nCapState = 0;
  1018.  
  1019.     // use OnSnap to set the UI state and the graphs
  1020.     //
  1021.     OnSnap( );
  1022. }
  1023.  
  1024. void CStillCapDlg::OnButtonReset() 
  1025. {
  1026.     // Reset bitmap counter to reset at zero
  1027.     m_nCapTimes = 0;
  1028. }
  1029.  
  1030. void CStillCapDlg::OnButtonViewstill() 
  1031. {
  1032.     // Open the bitmap with the system-default application
  1033.     ShellExecute(this->GetSafeHwnd(), TEXT("open\0"), mCB.m_szSnappedName, 
  1034.                  NULL, NULL, SW_SHOWNORMAL);
  1035. }
  1036.  
  1037. void CStillCapDlg::UpdateStatus(TCHAR *szStatus)
  1038. {
  1039.     m_StrStatus.SetWindowText(szStatus);
  1040. }
  1041.  
  1042. LRESULT CStillCapDlg::WindowProc(UINT message, WPARAM wParam, LPARAM lParam) 
  1043. {
  1044.     // Field the message posted by our SampleGrabber callback function.
  1045.     if (message == WM_CAPTURE_BITMAP)
  1046.         mCB.CopyBitmap(cb.dblSampleTime, cb.pBuffer, cb.lBufferSize);        
  1047.     
  1048.     return CDialog::WindowProc(message, wParam, lParam);
  1049. }
  1050.  
  1051. void CStillCapDlg::OnClose() 
  1052. {
  1053.     // Free the memory allocated for our bitmap data buffer
  1054.     if (cb.pBuffer != 0)
  1055.     {
  1056.         delete cb.pBuffer;
  1057.         cb.pBuffer = 0;
  1058.     }
  1059.         
  1060.     CDialog::OnClose();
  1061. }
  1062.