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