DecklinkIngestDlg.cpp

本文介绍了一个基于DirectShow的视频采集样本程序,详细展示了如何通过Blackmagic Design的Decklink设备进行视频和音频的数据捕获,并配置不同的压缩方式及文件写入策略。此外,还涉及了使用注册表保存偏好设置的方法。

  1. //-----------------------------------------------------------------------------   
  2. // $Id: DecklinkIngestDlg.cpp,v 1.1 2007/05/07 01:09:29 ivanr Exp $   
  3. //   
  4. // Desc: DirectShow ingest sample   
  5. //   
  6. // Copyright (c) Blackmagic Design 2007. All rights reserved.   
  7. //-----------------------------------------------------------------------------   
  8.    
  9. #include "stdafx.h"   
  10. #include "DecklinkIngest.h"   
  11. #include "DecklinkIngestDlg.h"   
  12. #include "PreferencesDlg.h"   
  13. #include "DeviceInfoDlg.h"   
  14. #include "Timecode.h"   
  15. #include "StaticURL.h"   
  16. #include <xprtdefs.h>   // device control   
  17. #include "CMX3600EDLReader.h"   
  18.    
  19. #include "DecklinkFilters_h.h"   
  20.    
  21. #ifdef _DEBUG   
  22. #define new DEBUG_NEW   
  23. #endif   
  24.    
  25. //-----------------------------------------------------------------------------   
  26. // CAboutDlg dialog used for App About   
  27. //-----------------------------------------------------------------------------   
  28. class CAboutDlg : public CDialog   
  29. {   
  30. public:   
  31.     CAboutDlg();   
  32.    
  33. // Dialog Data   
  34.     enum { IDD = IDD_ABOUTBOX };   
  35.    
  36.     protected:   
  37.     virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support   
  38.    
  39. // Implementation   
  40. protected:   
  41.     CStaticURL m_LinkBMD;   
  42.     CStaticURL m_LinkGDCL;   
  43.    
  44.     virtual BOOL OnInitDialog();   
  45.     DECLARE_MESSAGE_MAP()   
  46. };   
  47.    
  48. //-----------------------------------------------------------------------------   
  49. // Constructor   
  50. //   
  51. CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)   
  52. {   
  53. }   
  54.    
  55. //-----------------------------------------------------------------------------   
  56. // DoDataExchange   
  57. //   
  58. void CAboutDlg::DoDataExchange(CDataExchange* pDX)   
  59. {   
  60.     CDialog::DoDataExchange(pDX);   
  61. }   
  62.    
  63. //-----------------------------------------------------------------------------   
  64. // Message map   
  65. //   
  66. BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)   
  67. END_MESSAGE_MAP()   
  68.    
  69. //-----------------------------------------------------------------------------   
  70. // OnInitDialog   
  71. // Subclass the static controls that display URLs.   
  72. BOOL CAboutDlg::OnInitDialog()   
  73. {   
  74.     CDialog::OnInitDialog();   
  75.     m_LinkBMD.SubclassDlgItem(IDC_STATIC_URLBMD, this);   
  76.     m_LinkGDCL.SubclassDlgItem(IDC_STATIC_URLGDCL, this);   
  77.     return FALSE;   
  78. }   
  79.    
  80. //-----------------------------------------------------------------------------   
  81. // CDecklinkIngestDlg dialog   
  82. //-----------------------------------------------------------------------------   
  83. const int CDecklinkIngestDlg::MAXDEVICES = 256;   
  84.    
  85. static LPCWSTR SMARTT_NAME = L"Smart Tee";   
  86. static LPCWSTR VIDEORENDERER_NAME = L"Video Renderer";   
  87. static LPCWSTR SAMPLEGRABBERSTILLS_NAME = L"Sample Grabber Stills";   
  88. static LPCWSTR SAMPLEGRABBERTIMECODE_NAME = L"Sample Grabber Timecode";   
  89. static LPCWSTR NULLRENDERER_NAME = L"Null Renderer";   
  90.    
  91. static const int LogClipCtrlIDs[] = {IDC_BUTTON_REMOVECLIP, IDC_BUTTON_MOVECLIPUP, IDC_BUTTON_MOVECLIPDOWN};   
  92. static const int CaptureCtrlIDs[] = {IDC_BUTTON_CAPTURENOW, IDC_BUTTON_BATCHCAPTURE, IDC_BUTTON_CAPTURESTILL};   
  93. static const int TransportCtrlIDs[] = {IDC_BUTTON_FFWD, IDC_BUTTON_FREW, IDC_BUTTON_PLAY, IDC_BUTTON_STOP, IDC_BUTTON_EJECT, IDC_BUTTON_BATCHCAPTURE };   
  94.    
  95. //-----------------------------------------------------------------------------   
  96. // Constructor   
  97. //   
  98. CDecklinkIngestDlg::CDecklinkIngestDlg(CWnd* pParent /*=NULL*/)   
  99.     : CDialog(CDecklinkIngestDlg::IDD, pParent)   
  100. {   
  101.     m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);   
  102. }   
  103.    
  104. //-----------------------------------------------------------------------------   
  105. // DoDataExchange   
  106. //   
  107. void CDecklinkIngestDlg::DoDataExchange(CDataExchange* pDX)   
  108. {   
  109.     CDialog::DoDataExchange(pDX);   
  110.     DDX_Control(pDX, IDC_BUTTON_LOGCLIP, m_BtnAddClip);   
  111.     DDX_Control(pDX, IDC_BUTTON_REMOVECLIP, m_BtnRemoveClip);   
  112.     DDX_Control(pDX, IDC_BUTTON_MOVECLIPUP, m_BtnMoveClipUp);   
  113.     DDX_Control(pDX, IDC_BUTTON_MOVECLIPDOWN, m_BtnMoveClipDown);   
  114.     DDX_Control(pDX, IDC_LIST_CLIPS, m_ClipList);   
  115.     DDX_Control(pDX, IDC_STATIC_TIMECODE, m_TimecodeCtrl);   
  116.     DDX_Control(pDX, IDC_EDIT_INPOINT, m_InpointCtrl);   
  117.     DDX_Control(pDX, IDC_EDIT_OUTPOINT, m_OutpointCtrl);   
  118.     DDX_Control(pDX, IDC_STATIC_DURATION, m_DurationCtrl);   
  119.     DDX_Control(pDX, IDC_BUTTON_FREW, m_BtnFRew);   
  120.     DDX_Control(pDX, IDC_BUTTON_PLAY, m_BtnPlay);   
  121.     DDX_Control(pDX, IDC_BUTTON_FFWD, m_BtnFFwd);   
  122.     DDX_Control(pDX, IDC_BUTTON_STOP, m_BtnStop);   
  123.     DDX_Control(pDX, IDC_BUTTON_EJECT, m_BtnEjct);   
  124.     DDX_Control(pDX, IDC_EDIT_TAPENAME, m_TapenameCtrl);   
  125.     DDX_Control(pDX, IDC_EDIT_CLIPNAME, m_ClipnameCtrl);   
  126. }   
  127.    
  128. //-----------------------------------------------------------------------------   
  129. // Message map   
  130. //   
  131. BEGIN_MESSAGE_MAP(CDecklinkIngestDlg, CDialog)   
  132.     ON_WM_SYSCOMMAND()   
  133.     ON_WM_PAINT()   
  134.     ON_WM_QUERYDRAGICON()   
  135.     ON_COMMAND(ID_FILE_IMPORTEDL, OnFileImportEDL)   
  136.     ON_COMMAND(ID_EDIT_PREFERENCES, OnEditPreferences)   
  137.     ON_COMMAND_RANGE(IDC_DEVICE_BASE, IDC_DEVICE_BASE + CDecklinkIngestDlg::MAXDEVICES - 1, OnDeviceMenu)   
  138.     ON_COMMAND(IDC_DEVICE_INFO, OnDeviceInfo)   
  139.     ON_COMMAND(IDM_ABOUTBOX, OnAboutBox)   
  140.     ON_WM_INITMENUPOPUP()   
  141.     //}}AFX_MSG_MAP   
  142.     ON_BN_CLICKED(IDC_BUTTON_LOGCLIP, OnBnClickedButtonLogclip)   
  143.     ON_BN_CLICKED(IDC_BUTTON_REMOVECLIP, OnBnClickedButtonRemoveclip)   
  144.     ON_BN_CLICKED(IDC_BUTTON_MOVECLIPUP, OnBnClickedButtonMoveclipup)   
  145.     ON_BN_CLICKED(IDC_BUTTON_MOVECLIPDOWN, OnBnClickedButtonMoveclipdown)   
  146.     ON_BN_CLICKED(IDC_BUTTON_CAPTURESTILL, OnBnClickedButtonCaptureStill)   
  147.     ON_BN_CLICKED(IDC_BUTTON_CAPTURENOW, OnBnClickedButtonCaptureNow)   
  148.     ON_BN_CLICKED(IDC_BUTTON_BATCHCAPTURE, OnBnClickedButtonBatchCapture)   
  149.     ON_BN_CLICKED(IDC_BUTTON_ABORT, OnBnClickedButtonAbort)   
  150.     ON_BN_CLICKED(IDC_BUTTON_PLAY, OnBnClickedButtonPlay)   
  151.     ON_BN_CLICKED(IDC_BUTTON_STOP, OnBnClickedButtonStop)   
  152.     ON_BN_CLICKED(IDC_BUTTON_FREW, OnBnClickedButtonFrew)   
  153.     ON_BN_CLICKED(IDC_BUTTON_FFWD, OnBnClickedButtonFfwd)   
  154.     ON_BN_CLICKED(IDC_BUTTON_EJECT, OnBnClickedButtonEject)   
  155. END_MESSAGE_MAP()   
  156.    
  157. //-----------------------------------------------------------------------------   
  158. // OnInitDialog   
  159. //   
  160. BOOL CDecklinkIngestDlg::OnInitDialog()   
  161. {   
  162.     CDialog::OnInitDialog();   
  163.    
  164.     // Add "About..." menu item to system menu.   
  165.    
  166.     // IDM_ABOUTBOX must be in the system command range.   
  167.     ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);   
  168.     ASSERT(IDM_ABOUTBOX < 0xF000);   
  169.    
  170.     CMenu* pSysMenu = GetSystemMenu(FALSE);   
  171.     if (pSysMenu != NULL)   
  172.     {   
  173.         CString strAboutMenu;   
  174.         strAboutMenu.LoadString(IDS_ABOUTBOX);   
  175.         if (!strAboutMenu.IsEmpty())   
  176.         {   
  177.             pSysMenu->AppendMenu(MF_SEPARATOR);   
  178.             pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);   
  179.         }   
  180.     }   
  181.    
  182.     // Set the icon for this dialog.  The framework does this automatically   
  183.     //  when the application's main window is not a dialog   
  184.     SetIcon(m_hIcon, TRUE);         // Set big icon   
  185.     SetIcon(m_hIcon, FALSE);        // Set small icon   
  186.    
  187.     // TODO: Add extra initialization here   
  188.     // Create the sample grabber callback handlers.   
  189.     m_pStillGrabber = new CStillGrabber;   
  190.     m_pTimecodeGrabber = new CTimecodeGrabber;   
  191.    
  192.     m_pDeviceNameVideo = NULL;   
  193.    
  194.     CreateStatusBarControl();   
  195.    
  196.     m_hThread = NULL;   
  197.     m_hExitEvent = NULL;   
  198.    
  199.     // Set the defaults capture location and filename.   
  200.     TCHAR szPath[MAX_PATH] = {0};   
  201.     if (S_OK == SHGetFolderPath(NULL, CSIDL_PERSONAL, NULL, SHGFP_TYPE_CURRENT, szPath))   
  202.     {   
  203.         if (INVALID_FILE_ATTRIBUTES != GetFileAttributes(szPath))   
  204.         {   
  205.             PathAppend(szPath, TEXT("My Videos"));   
  206.             m_CaptureFilePath = szPath;   
  207.         }   
  208.     }   
  209.     m_CaptureFilename = TEXT("DecklinkIngest");   
  210.     m_CaptureTapename = TEXT("Untitled Tape");   
  211.    
  212.     // Set the default video capture format.   
  213.     m_VideoFormat.InitMediaType();   
  214.     m_VideoFormat.SetType(&MEDIATYPE_Video);   
  215.     m_VideoFormat.SetSubtype(&MEDIASUBTYPE_UYVY);   
  216.     m_VideoFormat.SetFormatType(&FORMAT_VideoInfo);   
  217.     VIDEOINFOHEADER* pvih = (VIDEOINFOHEADER*)m_VideoFormat.AllocFormatBuffer(sizeof(VIDEOINFOHEADER));   
  218.     if (pvih)   
  219.     {   
  220.         ZeroMemory(pvih, sizeof(VIDEOINFOHEADER));   
  221.         pvih->AvgTimePerFrame = 333667;   
  222.         pvih->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);   
  223.         pvih->bmiHeader.biWidth = 720;   
  224.         pvih->bmiHeader.biHeight = 486;   
  225.         pvih->bmiHeader.biPlanes = 1;   
  226.         pvih->bmiHeader.biBitCount = 16;   
  227.         pvih->bmiHeader.biCompression = 'YVYU';   
  228.         pvih->bmiHeader.biSizeImage = pvih->bmiHeader.biWidth * pvih->bmiHeader.biHeight * pvih->bmiHeader.biBitCount / 8;   
  229.         m_VideoFormat.SetSampleSize(pvih->bmiHeader.biSizeImage);   
  230.         pvih->dwBitRate = pvih->bmiHeader.biSizeImage * 8 * 30;   
  231.     }   
  232.    
  233.     // Set the default audio capture format.   
  234.     m_AudioFormat.InitMediaType();   
  235.     m_AudioFormat.SetType(&MEDIATYPE_Audio);   
  236.     m_AudioFormat.SetSubtype(&MEDIASUBTYPE_PCM);   
  237.     m_AudioFormat.SetFormatType(&FORMAT_WaveFormatEx);   
  238.     WAVEFORMATEX* pwfex = (WAVEFORMATEX*)m_AudioFormat.AllocFormatBuffer(sizeof(WAVEFORMATEX));   
  239.     if (pwfex)   
  240.     {   
  241.         ZeroMemory(pwfex, sizeof(WAVEFORMATEX));   
  242.         pwfex->wFormatTag = WAVE_FORMAT_PCM;   
  243.         pwfex->nChannels = 2;   
  244.         pwfex->nSamplesPerSec = 48000;   
  245.         pwfex->wBitsPerSample = 16;   
  246.         pwfex->nBlockAlign = pwfex->wBitsPerSample * pwfex->nChannels / 8;   
  247.         pwfex->nAvgBytesPerSec = pwfex->nSamplesPerSec * pwfex->nBlockAlign;   
  248.     }   
  249.    
  250.     m_bMuteAudio = false;   
  251.     m_Compression = ENC_NONE;   
  252.     m_Device = 0;   
  253.        
  254.     ResetFileIndex();   
  255.    
  256.     // load bitmaps for file buttons   
  257.     m_BtnAddClip.LoadBitmaps(IDB_BITMAP_ADDFILEU, IDB_BITMAP_ADDFILED, IDB_BITMAP_ADDFILEF, IDB_BITMAP_ADDFILEX);   
  258.     m_BtnRemoveClip.LoadBitmaps(IDB_BITMAP_REMOVEFILEU, IDB_BITMAP_REMOVEFILED, IDB_BITMAP_REMOVEFILEF, IDB_BITMAP_REMOVEFILEX);   
  259.     m_BtnMoveClipUp.LoadBitmaps(IDB_BITMAP_MOVEUPFILEU, IDB_BITMAP_MOVEUPFILED, IDB_BITMAP_MOVEUPFILEF, IDB_BITMAP_MOVEUPFILEX);   
  260.     m_BtnMoveClipDown.LoadBitmaps(IDB_BITMAP_MOVEDOWNFILEU, IDB_BITMAP_MOVEDOWNFILED, IDB_BITMAP_MOVEDOWNFILEF, IDB_BITMAP_MOVEDOWNFILEX);   
  261.    
  262.     // set the columns in the list control   
  263.     int ColumnWidth = m_ClipList.GetStringWidth(TEXT("00:00:00:00")) + 12;   
  264.     m_ClipList.InsertColumn(0, TEXT("Clip name"), LVCFMT_CENTER, 150);   
  265.     m_ClipList.InsertColumn(1, TEXT("In-point"), LVCFMT_CENTER, ColumnWidth);   
  266.     m_ClipList.InsertColumn(2, TEXT("Out-point"), LVCFMT_CENTER, ColumnWidth);   
  267.     m_ClipList.InsertColumn(3, TEXT("Duration"), LVCFMT_CENTER, ColumnWidth);   
  268.     m_ClipList.InsertColumn(4, TEXT("Tape name"), LVCFMT_CENTER, 150);   
  269.    
  270.     m_InpointCtrl.SetWindowText(TEXT("00:00:00:00"));   
  271.     m_OutpointCtrl.SetWindowText(TEXT("00:00:00:00"));   
  272.     m_TapenameCtrl.SetWindowText(TEXT("Untitled Tape"));   
  273.     m_ClipnameCtrl.SetWindowText(TEXT("Untitled Clip"));   
  274.    
  275.     // load bitmaps for the deck controls   
  276.     m_BtnFRew.LoadBitmaps(IDB_BITMAP_FREWU, IDB_BITMAP_FREWD, IDB_BITMAP_FREWF, IDB_BITMAP_FREWX);   
  277.     m_BtnPlay.LoadBitmaps(IDB_BITMAP_PLAYU, IDB_BITMAP_PLAYD, IDB_BITMAP_PLAYF, IDB_BITMAP_PLAYX);   
  278.     m_BtnFFwd.LoadBitmaps(IDB_BITMAP_FFWDU, IDB_BITMAP_FFWDD, IDB_BITMAP_FFWDF, IDB_BITMAP_FFWDX);   
  279.     m_BtnStop.LoadBitmaps(IDB_BITMAP_STOPU, IDB_BITMAP_STOPD, IDB_BITMAP_STOPF, IDB_BITMAP_STOPX);   
  280.     m_BtnEjct.LoadBitmaps(IDB_BITMAP_EJECTU, IDB_BITMAP_EJECTD, IDB_BITMAP_EJECTF, IDB_BITMAP_EJECTX);   
  281.    
  282.     m_bBatchCapture = false;   
  283.    
  284.     QueryRegistry();    // Retrieve the previous state.   
  285.    
  286.     SetFileExtension(m_CaptureFilename);   
  287.    
  288.     CreateDeviceMenu(); // Discover system capture devices and create appropriate menu items.   
  289. #ifdef _DEBUG   
  290.     m_ClipList.AddItem(new CClip(TEXT("Tape 1"), TEXT("Untitled Clip 1"), TEXT("01:01:00:00"), TEXT("01:01:09:24"), (WORD)(UNITS / CUtils::GetAvgTimePerFrame(&m_VideoFormat))));   
  291.     m_ClipList.AddItem(new CClip(TEXT("Tape 1"), TEXT("Untitled Clip 2"), TEXT("01:01:30:00"), TEXT("01:01:39:24"), (WORD)(UNITS / CUtils::GetAvgTimePerFrame(&m_VideoFormat))));   
  292.     m_ClipList.AddItem(new CClip(TEXT("Tape 1"), TEXT("Untitled Clip 3"), TEXT("01:02:00:00"), TEXT("01:02:09:24"), (WORD)(UNITS / CUtils::GetAvgTimePerFrame(&m_VideoFormat))));   
  293.     m_ClipList.AddItem(new CClip(TEXT("Tape 1"), TEXT("Untitled Clip 4"), TEXT("01:02:30:00"), TEXT("01:02:39:24"), (WORD)(UNITS / CUtils::GetAvgTimePerFrame(&m_VideoFormat))));   
  294.     m_ClipList.AddItem(new CClip(TEXT("Tape 1"), TEXT("Untitled Clip 5"), TEXT("01:03:00:00"), TEXT("01:03:09:24"), (WORD)(UNITS / CUtils::GetAvgTimePerFrame(&m_VideoFormat))));   
  295.     m_ClipList.AddItem(new CClip(TEXT("Tape 1"), TEXT("Untitled Clip 6"), TEXT("01:03:30:00"), TEXT("01:03:39:24"), (WORD)(UNITS / CUtils::GetAvgTimePerFrame(&m_VideoFormat))));   
  296.     m_ClipList.AddItem(new CClip(TEXT("Tape 1"), TEXT("Untitled Clip 7"), TEXT("01:04:00:00"), TEXT("01:04:09:24"), (WORD)(UNITS / CUtils::GetAvgTimePerFrame(&m_VideoFormat))));   
  297.     m_ClipList.AddItem(new CClip(TEXT("Tape 1"), TEXT("Untitled Clip 8"), TEXT("01:04:30:00"), TEXT("01:04:39:24"), (WORD)(UNITS / CUtils::GetAvgTimePerFrame(&m_VideoFormat))));   
  298.     m_ClipList.AddItem(new CClip(TEXT("Tape 1"), TEXT("Untitled Clip 9"), TEXT("01:05:00:00"), TEXT("01:05:09:24"), (WORD)(UNITS / CUtils::GetAvgTimePerFrame(&m_VideoFormat))));   
  299.     m_ClipList.AddItem(new CClip(TEXT("Tape 1"), TEXT("Untitled Clip 10"), TEXT("01:05:30:00"), TEXT("01:05:39:24"), (WORD)(UNITS / CUtils::GetAvgTimePerFrame(&m_VideoFormat))));   
  300.     m_ClipList.AddItem(new CClip(TEXT("Tape 1"), TEXT("Untitled Clip 11"), TEXT("01:06:00:00"), TEXT("01:06:09:24"), (WORD)(UNITS / CUtils::GetAvgTimePerFrame(&m_VideoFormat))));   
  301.     m_ClipList.AddItem(new CClip(TEXT("Tape 1"), TEXT("Untitled Clip 12"), TEXT("01:06:30:00"), TEXT("01:06:39:24"), (WORD)(UNITS / CUtils::GetAvgTimePerFrame(&m_VideoFormat))));   
  302.     m_ClipList.AddItem(new CClip(TEXT("Tape 1"), TEXT("Untitled Clip 13"), TEXT("01:07:00:00"), TEXT("01:07:09:24"), (WORD)(UNITS / CUtils::GetAvgTimePerFrame(&m_VideoFormat))));   
  303.     m_ClipList.AddItem(new CClip(TEXT("Tape 1"), TEXT("Untitled Clip 14"), TEXT("01:07:30:00"), TEXT("01:07:39:24"), (WORD)(UNITS / CUtils::GetAvgTimePerFrame(&m_VideoFormat))));   
  304.     m_ClipList.AddItem(new CClip(TEXT("Tape 1"), TEXT("Untitled Clip 15"), TEXT("01:08:00:00"), TEXT("01:08:09:24"), (WORD)(UNITS / CUtils::GetAvgTimePerFrame(&m_VideoFormat))));   
  305.     m_ClipList.AddItem(new CClip(TEXT("Tape 1"), TEXT("Untitled Clip 16"), TEXT("01:08:30:00"), TEXT("01:08:39:24"), (WORD)(UNITS / CUtils::GetAvgTimePerFrame(&m_VideoFormat))));   
  306.     m_ClipList.AddItem(new CClip(TEXT("Tape 1"), TEXT("Untitled Clip 17"), TEXT("01:09:00:00"), TEXT("01:09:09:24"), (WORD)(UNITS / CUtils::GetAvgTimePerFrame(&m_VideoFormat))));   
  307.     m_ClipList.AddItem(new CClip(TEXT("Tape 1"), TEXT("Untitled Clip 18"), TEXT("01:09:30:00"), TEXT("01:09:39:24"), (WORD)(UNITS / CUtils::GetAvgTimePerFrame(&m_VideoFormat))));   
  308.     m_ClipList.AddItem(new CClip(TEXT("Tape 1"), TEXT("Untitled Clip 19"), TEXT("01:10:00:00"), TEXT("01:10:09:24"), (WORD)(UNITS / CUtils::GetAvgTimePerFrame(&m_VideoFormat))));   
  309.     m_ClipList.AddItem(new CClip(TEXT("Tape 1"), TEXT("Untitled Clip 20"), TEXT("01:10:30:00"), TEXT("01:10:39:24"), (WORD)(UNITS / CUtils::GetAvgTimePerFrame(&m_VideoFormat))));   
  310. #endif   
  311.     // Create a source side capture graph for previewing the audio and video inputs.   
  312.     HRESULT hr = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, __uuidof(IGraphBuilder), reinterpret_cast<void**>(&m_pSourceGraph));   
  313.     if (SUCCEEDED(hr))   
  314.     {   
  315. #ifdef _DEBUG   
  316.         hr = CDSUtils::AddGraphToRot(m_pSourceGraph, &m_ROTRegisterSource);   
  317. #endif   
  318.         m_pIMediaEvent = m_pSourceGraph;   
  319.         ASSERT(m_pIMediaEvent);   
  320.    
  321.         if (SUCCEEDED(hr))   
  322.         {   
  323.             hr = CDSUtils::AddFilter(m_pSourceGraph, CLSID_SmartTee, SMARTT_NAME, &m_pSmartT);   
  324.             if (SUCCEEDED(hr))   
  325.             {   
  326.                 // Locate video screen renderer for the preview window.   
  327.                 hr = CDSUtils::AddFilter(m_pSourceGraph, CLSID_VideoRendererDefault, VIDEORENDERER_NAME, &m_pVideoRenderer);   
  328.                 if (SUCCEEDED(hr))   
  329.                 {   
  330.                     // Configure the renderer to be in windowless mode.   
  331.                     CComQIPtr<ivmrfilterconfig, &iid_ivmrfilterconfig=""> pIVMRFilterConfig = m_pVideoRenderer;   
  332.                     if (pIVMRFilterConfig)   
  333.                     {   
  334.                         hr = pIVMRFilterConfig->SetRenderingMode(VMRMode_Windowless);   
  335.                     }   
  336.    
  337.                     // Configure the renderer to be in windowless mode.   
  338.                     CComQIPtr<ivmrwindowlesscontrol, &iid_ivmrwindowlesscontrol=""> pIVMRWindowlessCtrl = m_pVideoRenderer;   
  339.                     if (pIVMRWindowlessCtrl)   
  340.                     {   
  341.                         CWnd* pWnd = GetDlgItem(IDC_STATIC_PREVIEW);   
  342.                         hr = pIVMRWindowlessCtrl->SetVideoClippingWindow(pWnd->GetSafeHwnd());    // Set the bounds of the video to the preview window.   
  343.                         RECT rcDst = {0};   
  344.                         pWnd->GetClientRect(&rcDst);   
  345.                         SetRect(&rcDst, 0, 0, rcDst.right, rcDst.bottom);   
  346.                         hr = pIVMRWindowlessCtrl->SetVideoPosition(NULL, &rcDst);    // Show the whole of the source frame in the whole of the client area of the control.   
  347.                         hr = pIVMRWindowlessCtrl->SetAspectRatioMode(VMR_ARMODE_LETTER_BOX); // Maintain the aspect ratio of the video.   
  348.                         hr = pIVMRWindowlessCtrl->SetBorderColor(GetSysColor(COLOR_BTNFACE));    // Set the colour of the letter or pillar boxed area.   
  349.                     }   
  350.                 }   
  351.    
  352.                 // The sample grabber can be used for capturing a still during preview.   
  353.                 if (SUCCEEDED(hr))   
  354.                 {   
  355.                     hr = CDSUtils::AddFilter(m_pSourceGraph, CLSID_SampleGrabber, SAMPLEGRABBERSTILLS_NAME, &m_pSampleGrabberStills);   
  356.                     if (SUCCEEDED(hr))   
  357.                     {   
  358.                         // query for the sample grabber interface   
  359.                         m_pISampleGrabberStills = m_pSampleGrabberStills;   
  360.                         ASSERT(m_pISampleGrabberStills);   
  361.                     }   
  362.                 }   
  363.    
  364.                 // The sample grabber can be used for capturing timecode for frame accurate capture.   
  365.                 if (SUCCEEDED(hr))   
  366.                 {   
  367.                     hr = CDSUtils::AddFilter(m_pSourceGraph, CLSID_NullRenderer, NULLRENDERER_NAME, &m_pNullRenderer);   
  368.                     if (SUCCEEDED(hr))   
  369.                     {   
  370.                         hr = CDSUtils::AddFilter(m_pSourceGraph, CLSID_SampleGrabber, SAMPLEGRABBERTIMECODE_NAME, &m_pSampleGrabberTimecode);   
  371.                         if (SUCCEEDED(hr))   
  372.                         {   
  373.                             // query for the sample grabber interface   
  374.                             m_pISampleGrabberTimecode = m_pSampleGrabberTimecode;   
  375.                             if (m_pISampleGrabberTimecode)   
  376.                             {   
  377.                                 hr = m_pISampleGrabberTimecode->SetCallback(m_pTimecodeGrabber, 0);   
  378.                             }   
  379.                         }   
  380.                     }   
  381.                 }   
  382.    
  383.                 OnDeviceMenu(IDC_DEVICE_BASE + m_Device);   // Set the device name and build a preview graph.   
  384.             }   
  385.         }   
  386.     }   
  387.    
  388.     if (SUCCEEDED(hr))   
  389.     {   
  390.         // Create a sink side capture graph for writing the audio and video to disk.   
  391.         hr = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, __uuidof(IGraphBuilder), reinterpret_cast<void**>(&m_pSinkGraph));   
  392.         if (SUCCEEDED(hr))   
  393.         {   
  394. #ifdef _DEBUG   
  395.             hr = CDSUtils::AddGraphToRot(m_pSinkGraph, &m_ROTRegisterSink);   
  396. #endif   
  397.         }   
  398.     }   
  399.    
  400.     return TRUE;  // return TRUE  unless you set the focus to a control   
  401. }   
  402.    
  403. //-----------------------------------------------------------------------------   
  404. // DestroyWindow   
  405. // Called when the window is being destroyed, clean up and free all resources.   
  406. BOOL CDecklinkIngestDlg::DestroyWindow()   
  407. {   
  408. #ifdef _DEBUG   
  409.     CDSUtils::RemoveGraphFromRot(m_ROTRegisterSource);   
  410.     CDSUtils::RemoveGraphFromRot(m_ROTRegisterSink);   
  411. #endif   
  412.     // Cancel the sample grabber callbacks.   
  413.     EnableSampleGrabberCallback(false);   
  414.     if (m_pISampleGrabberTimecode)   
  415.     {   
  416.         m_pISampleGrabberTimecode->SetCallback(NULL, 0);   
  417.     }   
  418.    
  419.     DestroyThreads();   // Destroy the thread which uses the source interfaces that are about to be released.   
  420.     DestroySinkGraph();   
  421.     DestroySourceGraph();   
  422.    
  423.     m_RegUtils.Close();   
  424.    
  425.     // Release the filter names attached to the menu item's data   
  426.     CMenu* pMenu = GetMenu();   
  427.     int Item = FindMenuItem(pMenu, TEXT("&Device"));   
  428.     if (-1 != Item)   
  429.     {   
  430.         CMenu* pSubMenu = pMenu->GetSubMenu(Item);   
  431.         int Count = pSubMenu->GetMenuItemCount();   
  432.         for (int Device=0; Device<count; ++device)="" {="" uint="" state="pSubMenu-">GetMenuState(IDC_DEVICE_BASE + Device, MF_BYCOMMAND);   
  433.             if (0xFFFFFFFF != State)   
  434.             {   
  435.                 MENUITEMINFO MenuItemInfo = {0};   
  436.                 MenuItemInfo.cbSize = sizeof(MenuItemInfo);   
  437.                 MenuItemInfo.fMask = MIIM_DATA;   
  438.                 if (pSubMenu->GetMenuItemInfo(IDC_DEVICE_BASE + Device, &MenuItemInfo))   
  439.                 {   
  440.                     delete [] (LPWSTR*)MenuItemInfo.dwItemData;   
  441.                 }   
  442.             }   
  443.         }   
  444.     }   
  445.    
  446.     int cItems = m_ClipList.GetItemCount();   
  447.     for (int item=0; item<citems; ++item)="" {="" cclip*="" pclip="reinterpret_cast<CClip*">(m_ClipList.GetItemData(item));   
  448.         if (pClip)   
  449.         {   
  450.             delete pClip;   
  451.         }   
  452.     }   
  453.    
  454.     m_ClipList.DeleteAllItems();   
  455.    
  456.     SAFE_DELETE(m_pTimecodeGrabber);   
  457.     SAFE_DELETE(m_pStillGrabber);   
  458.    
  459.     return CDialog::DestroyWindow();   
  460. }   
  461.    
  462. //-----------------------------------------------------------------------------   
  463. // OnSysCommand   
  464. //   
  465. void CDecklinkIngestDlg::OnSysCommand(UINT nID, LPARAM lParam)   
  466. {   
  467.     if ((nID & 0xFFF0) == IDM_ABOUTBOX)   
  468.     {   
  469.         CAboutDlg dlgAbout;   
  470.         dlgAbout.DoModal();   
  471.     }   
  472.     else   
  473.     {   
  474.         CDialog::OnSysCommand(nID, lParam);   
  475.     }   
  476. }   
  477.    
  478. //-----------------------------------------------------------------------------   
  479. // OnPaint   
  480. // If you add a minimize button to your dialog, you will need the code below   
  481. // to draw the icon.  For MFC applications using the document/view model,   
  482. // this is automatically done for you by the framework.   
  483. void CDecklinkIngestDlg::OnPaint()    
  484. {   
  485.     if (IsIconic())   
  486.     {   
  487.         CPaintDC dc(this); // device context for painting   
  488.    
  489.         SendMessage(WM_ICONERASEBKGND, reinterpret_cast<wparam>(dc.GetSafeHdc()), 0);   
  490.    
  491.         // Center icon in client rectangle   
  492.         int cxIcon = GetSystemMetrics(SM_CXICON);   
  493.         int cyIcon = GetSystemMetrics(SM_CYICON);   
  494.         CRect rect;   
  495.         GetClientRect(&rect);   
  496.         int x = (rect.Width() - cxIcon + 1) / 2;   
  497.         int y = (rect.Height() - cyIcon + 1) / 2;   
  498.    
  499.         // Draw the icon   
  500.         dc.DrawIcon(x, y, m_hIcon);   
  501.     }   
  502.     else   
  503.     {   
  504.         CDialog::OnPaint();   
  505.     }   
  506. }   
  507.    
  508. //-----------------------------------------------------------------------------   
  509. // OnQueryDragIcon   
  510. // The system calls this function to obtain the cursor to display while the user drags   
  511. //  the minimized window.   
  512. HCURSOR CDecklinkIngestDlg::OnQueryDragIcon()   
  513. {   
  514.     return static_cast<hcursor>(m_hIcon);   
  515. }   
  516.    
  517. //-----------------------------------------------------------------------------   
  518. // OnEditPreferences   
  519. // Display the preferences dialog.   
  520. void CDecklinkIngestDlg::OnFileImportEDL()   
  521. {   
  522.     char BASED_CODE szFilters[] = "Edit Decision List|*.edl|All Files (*.*)|*.*||";   
  523.    
  524.     CFileDialog FileDlg(TRUE, "Edit Decision Lists", TEXT("*.edl"), 0, szFilters, this);   
  525.    
  526.     if (FileDlg.DoModal() == IDOK)   
  527.     {   
  528.         try   
  529.         {   
  530.             HRESULT hr = S_OK;   
  531.             CBaseEDLReader* pEDLReader = new CCMX3600EDLReader(FileDlg.GetPathName(), &hr);   
  532.         }   
  533.         catch (std::bad_alloc)   
  534.         {   
  535.         }   
  536.     }   
  537. }   
  538.    
  539. //-----------------------------------------------------------------------------   
  540. // OnEditPreferences   
  541. // Display the preferences dialog.   
  542. void CDecklinkIngestDlg::OnEditPreferences()   
  543. {   
  544.     CPreferencesDlg dlg(m_CaptureFilePath, m_CaptureFilename, m_pVideoDevice, m_VideoFormat, m_pAudioDevice, m_AudioFormat, m_bMuteAudio, m_Compression);   
  545.     if (IDOK == dlg.DoModal())   
  546.     {   
  547.         // Set the correct file extension for the compression.   
  548.         m_Compression = dlg.GetCompression();   
  549.         SetFileExtension(m_CaptureFilename);   
  550.    
  551.         // Validate the capture folder location and reset the file index if the location has changed.   
  552.         basic_string<tchar> Temp = dlg.GetCaptureLocation();   
  553.         if ((INVALID_FILE_ATTRIBUTES != GetFileAttributes(Temp.c_str())) && (m_CaptureFilePath != Temp))   
  554.         {   
  555.             m_CaptureFilePath = Temp;   
  556.             ResetFileIndex();   
  557.         }   
  558.    
  559.         // Reset the file index if the base file name has changed.   
  560.         Temp = dlg.GetCaptureFilename();   
  561.         if (m_CaptureFilename != Temp)   
  562.         {   
  563.             m_CaptureFilename = Temp;   
  564.             ResetFileIndex();   
  565.         }   
  566.    
  567.         // Set the filename for the stills grabber.   
  568.         if (m_pStillGrabber)   
  569.         {   
  570.             m_pStillGrabber->SetFilename(m_CaptureFilename);   
  571.         }   
  572.    
  573.         // Rebuild the source graph if the audio or video formats or the audio preference has changed.   
  574.         CMediaType VideoFormat(dlg.GetVideoFormat());   
  575.         CMediaType AudioFormat(dlg.GetAudioFormat());   
  576.         bool bMuteAudio = dlg.GetMuteAudio();   
  577.         if (!(m_VideoFormat == VideoFormat) || !(m_AudioFormat == AudioFormat) || (m_bMuteAudio != bMuteAudio))   
  578.         {   
  579.             m_VideoFormat = VideoFormat;   
  580.             m_AudioFormat = AudioFormat;   
  581.             m_bMuteAudio = bMuteAudio;   
  582.             CreateCaptureSourceGraph();   
  583.         }   
  584.    
  585.         SavePreferencesToRegistry();   
  586.     }   
  587. }   
  588.    
  589. //-----------------------------------------------------------------------------   
  590. // OnDeviceMenu   
  591. // Change the device to be used for ingesting.   
  592. void CDecklinkIngestDlg::OnDeviceMenu(UINT nID)   
  593. {   
  594.     CMenu* pMenu = GetMenu();   
  595.     int Item = FindMenuItem(pMenu, TEXT("&Device"));   
  596.     if (-1 != Item)   
  597.     {   
  598.         // Update the menu with the new selection.   
  599.         CMenu* pSubMenu = pMenu->GetSubMenu(Item);   
  600.         pSubMenu->CheckMenuRadioItem(IDC_DEVICE_BASE, IDC_DEVICE_BASE + CDecklinkIngestDlg::MAXDEVICES - 1, nID, MF_BYCOMMAND);   
  601.    
  602.         // Update the currently selected filter name.   
  603.         int Count = pSubMenu->GetMenuItemCount();   
  604.         for (int Device=0; Device<count; ++device)="" {="" uint="" state="pSubMenu-">GetMenuState(IDC_DEVICE_BASE + Device, MF_BYCOMMAND);   
  605.             if ((0xFFFFFFFF != State) && (State & MF_CHECKED))   
  606.             {   
  607.                 MENUITEMINFO MenuItemInfo = {0};   
  608.                 MenuItemInfo.cbSize = sizeof(MenuItemInfo);   
  609.                 MenuItemInfo.fMask = MIIM_DATA;   
  610.                 if (pSubMenu->GetMenuItemInfo(IDC_DEVICE_BASE + Device, &MenuItemInfo))   
  611.                 {   
  612.                     // Retrieve the capture device name from the menu.   
  613.                     m_pDeviceNameVideo = (LPWSTR)MenuItemInfo.dwItemData;   
  614.    
  615.                     // Rebuild the source graph with the new device selection.   
  616.                     CreateCaptureSourceGraph();   
  617.                 }   
  618.    
  619.                 m_Device = Device;   
  620.                 EXECUTE_ASSERT(ERROR_SUCCESS == m_RegUtils.SetBinary(TEXT("VideoCaptureDevice"), reinterpret_cast<lpbyte>(&m_Device), sizeof(m_Device)));   
  621.                 break;   
  622.             }   
  623.         }   
  624.     }   
  625. }   
  626.    
  627. //-----------------------------------------------------------------------------   
  628. // OnDeviceInfo   
  629. // Display the IO features of the selected device.   
  630. void CDecklinkIngestDlg::OnDeviceInfo()   
  631. {   
  632.     CDeviceInfoDlg dlg(m_pDeviceNameVideo, &CLSID_VideoInputDeviceCategory);   
  633.     dlg.DoModal();   
  634. }   
  635.    
  636. //-----------------------------------------------------------------------------   
  637. // OnAboutBox   
  638. // Display the about box.   
  639. void CDecklinkIngestDlg::OnAboutBox()   
  640. {   
  641.     OnSysCommand(IDM_ABOUTBOX, 0);   
  642. }   
  643.    
  644. //-----------------------------------------------------------------------------   
  645. // OnBnClickedButtonLogclip   
  646. // Create a file browser dialog to retrieve the name of a clip to add to the filelist.   
  647. void CDecklinkIngestDlg::OnBnClickedButtonLogclip()   
  648. {   
  649.     CString Tapename, Clipname, Inpoint, Outpoint;   
  650.     m_TapenameCtrl.GetWindowText(Tapename);   
  651.     m_ClipnameCtrl.GetWindowText(Clipname);   
  652.     m_InpointCtrl.GetWindowText(Inpoint);   
  653.     m_OutpointCtrl.GetWindowText(Outpoint);   
  654.    
  655.     // Append an appropriate file extension.   
  656.     basic_string<tchar> clipname = Clipname;   
  657.     SetFileExtension(clipname);   
  658.    
  659.     try   
  660.     {   
  661.         m_ClipList.AddItem(new CClip(Tapename.GetBuffer(), clipname.c_str(), Inpoint.GetBuffer(), Outpoint.GetBuffer(), (WORD)(UNITS / CUtils::GetAvgTimePerFrame(&m_VideoFormat))));   
  662.         if (0 != m_ClipList.GetItemCount())   
  663.         {   
  664.             EnableWindows(LogClipCtrlIDs, SIZEOF_ARRAY(LogClipCtrlIDs), TRUE);   
  665.         }   
  666.     }   
  667.     catch (std::bad_alloc)   
  668.     {   
  669.         // memory allocation error   
  670.     }   
  671. }   
  672.    
  673. //-----------------------------------------------------------------------------   
  674. // OnBnClickedButtonRemovefile   
  675. // Remove the selected file from the filelist.   
  676. void CDecklinkIngestDlg::OnBnClickedButtonRemoveclip()   
  677. {   
  678.     m_ClipList.RemoveItem();   
  679.    
  680.     // if there are no items in the list, disable the export and stop buttons   
  681.     if (0 == m_ClipList.GetItemCount())   
  682.     {   
  683.         EnableWindows(LogClipCtrlIDs, SIZEOF_ARRAY(LogClipCtrlIDs), FALSE);   
  684.     }   
  685. }   
  686.    
  687. //-----------------------------------------------------------------------------   
  688. // OnBnClickedButtonMovefileup   
  689. // Move the selected file up in the filelist.   
  690. void CDecklinkIngestDlg::OnBnClickedButtonMoveclipup()   
  691. {   
  692.     m_ClipList.MoveItemUp();   
  693. }   
  694.    
  695. //-----------------------------------------------------------------------------   
  696. // OnBnClickedButtonMovefiledown   
  697. // Move the selected file down in the filelist.   
  698. void CDecklinkIngestDlg::OnBnClickedButtonMoveclipdown()   
  699. {   
  700.     m_ClipList.MoveItemDown();   
  701. }   
  702.    
  703. //-----------------------------------------------------------------------------   
  704. // OnBnClickedButtonPlay   
  705. //   
  706. void CDecklinkIngestDlg::OnBnClickedButtonPlay()   
  707. {   
  708.     if (m_pIExtTransport)   
  709.     {   
  710.         m_pIExtTransport->put_Mode(ED_MODE_PLAY);   
  711.     }   
  712. }   
  713.    
  714. //-----------------------------------------------------------------------------   
  715. // OnBnClickedButtonStop   
  716. //   
  717. void CDecklinkIngestDlg::OnBnClickedButtonStop()   
  718. {   
  719.     if (m_pIExtTransport)   
  720.     {   
  721.         m_pIExtTransport->put_Mode(ED_MODE_STOP);   
  722.     }   
  723. }   
  724.    
  725. //-----------------------------------------------------------------------------   
  726. // OnBnClickedButtonFrew   
  727. //   
  728. void CDecklinkIngestDlg::OnBnClickedButtonFrew()   
  729. {   
  730.     if (m_pIExtTransport)   
  731.     {   
  732.         m_pIExtTransport->put_Mode(ED_MODE_REW);   
  733.     }   
  734. }   
  735.    
  736. //-----------------------------------------------------------------------------   
  737. // OnBnClickedButtonFfwd   
  738. //   
  739. void CDecklinkIngestDlg::OnBnClickedButtonFfwd()   
  740. {   
  741.     if (m_pIExtTransport)   
  742.     {   
  743.         m_pIExtTransport->put_Mode(ED_MODE_FF);   
  744.     }   
  745. }   
  746.    
  747. //-----------------------------------------------------------------------------   
  748. // OnBnClickedButtonEject   
  749. //   
  750. void CDecklinkIngestDlg::OnBnClickedButtonEject()   
  751. {   
  752.     if (m_pIExtTransport)   
  753.     {   
  754.         m_pIExtTransport->put_MediaState(ED_MEDIA_UNLOAD);   
  755.     }   
  756. }   
  757.    
  758. //-----------------------------------------------------------------------------   
  759. // OnBnClickedButtonCaptureStill   
  760. //   
  761. void CDecklinkIngestDlg::OnBnClickedButtonCaptureStill()   
  762. {   
  763.     if (m_pStillGrabber)   
  764.     {   
  765.         m_pStillGrabber->Capture();   
  766.            
  767.         // Create a string for the status control.   
  768.         basic_string<tchar> Message = TEXT("Capturing still to: /"/"");   
  769.         basic_string<tchar>::size_type Index = Message.rfind(_T('/"'));   
  770.         Message.insert(Index, m_pStillGrabber->GetFilePath());   
  771.         VERIFY(m_StatusBarCtrl.SetText(Message.c_str(), 0, 0));   
  772.     }   
  773. }   
  774.    
  775. //-----------------------------------------------------------------------------   
  776. // OnBnClickedButtonCaptureNow   
  777. // Start capturing from the device immediately.   
  778. void CDecklinkIngestDlg::OnBnClickedButtonCaptureNow()   
  779. {   
  780.     TCHAR szCaptureFilename[MAX_PATH] = {0};   
  781.     StringCchPrintf(szCaptureFilename, MAX_PATH, TEXT("%s %d"), m_CaptureFilename.c_str(), m_FileIndex++);   
  782.    
  783.     if (SUCCEEDED(CreateCaptureSinkGraph(szCaptureFilename)))   
  784.     {   
  785.         // Start the capture streams.   
  786.         REFERENCE_TIME rtStop = MAXLONGLONG;   
  787.         if (SUCCEEDED(ConfigureSourceGraph(NULL, &rtStop))) // Start the streams immediately and cancel any pending stop requests.   
  788.         {   
  789.             EnableSampleGrabberCallback(false); // Cancel the sample grabber callback during capture.   
  790.    
  791.             StartCapture();   
  792.    
  793.             EnableWindows(CaptureCtrlIDs, SIZEOF_ARRAY(CaptureCtrlIDs), FALSE);   
  794.             CWnd* pWnd = GetDlgItem(IDC_BUTTON_ABORT);   
  795.             pWnd->EnableWindow(TRUE);   
  796.             pWnd->SetWindowText(TEXT("Stop"));   
  797.    
  798.             // Create a string for the status control.   
  799.             basic_string<tchar> Message = TEXT("Capturing to: /"/"");   
  800.             basic_string<tchar>::size_type Index = Message.rfind(_T('/"'));   
  801.             Message.insert(Index, szCaptureFilename);   
  802.             VERIFY(m_StatusBarCtrl.SetText(Message.c_str(), 0, 0));   
  803.         }   
  804.     }   
  805. }   
  806.    
  807. //-----------------------------------------------------------------------------   
  808. // OnBnClickedButtonBatchCapture   
  809. // Start capturing from the device using the batch list.   
  810. void CDecklinkIngestDlg::OnBnClickedButtonBatchCapture()   
  811. {   
  812.     CClip* pClip = reinterpret_cast<cclip*>(m_ClipList.GetItemData(0));   
  813.     basic_string<tchar> Message = TEXT("Please load the tape ");   
  814.     Message.append(pClip->Tapename());   
  815.     Message.append(TEXT(" into the deck."));   
  816.     if (IDOK == MessageBox(Message.c_str(), TEXT("Check/Load tape"), MB_ICONEXCLAMATION | MB_OKCANCEL))   
  817.     {   
  818.         m_CaptureTapename = pClip->Tapename();   
  819.            
  820.         EnableSampleGrabberCallback(false); // Cancel the sample grabber callback during capture.   
  821.    
  822.         EnableWindows(CaptureCtrlIDs, SIZEOF_ARRAY(CaptureCtrlIDs), FALSE);   
  823.         CWnd* pWnd = GetDlgItem(IDC_BUTTON_ABORT);   
  824.         pWnd->EnableWindow(TRUE);   
  825.         pWnd->SetWindowText(TEXT("Abort"));   
  826.    
  827.         m_ClipList.SetSelectedItem(0);   
  828.    
  829.         m_bBatchCapture = true;   
  830.     }   
  831. }   
  832.    
  833. //-----------------------------------------------------------------------------   
  834. // OnBnClickedButtonAbort   
  835. // Stop capture.   
  836. void CDecklinkIngestDlg::OnBnClickedButtonAbort()   
  837. {   
  838.     m_bBatchCapture = false;   
  839.    
  840.     StopCapture();   
  841.    
  842.     EnableSampleGrabberCallback(true);  // Enable the sample grabber callback during preview.   
  843.    
  844.     EnableWindows(CaptureCtrlIDs, SIZEOF_ARRAY(CaptureCtrlIDs), TRUE);   
  845.     CWnd* pWnd = GetDlgItem(IDC_BUTTON_ABORT);   
  846.     pWnd->EnableWindow(FALSE);   
  847.     pWnd->SetWindowText(TEXT("Abort"));   
  848.    
  849.     VERIFY(m_StatusBarCtrl.SetText(TEXT("Capture stopped."), 0, 0));   
  850.     m_ProgressCtrl.ShowWindow(SW_HIDE);   
  851. }   
  852.    
  853. //-----------------------------------------------------------------------------   
  854. // Private methods.   
  855. //-----------------------------------------------------------------------------   
  856. //-----------------------------------------------------------------------------   
  857. // CreateDeviceMenu   
  858. //   
  859. HRESULT CDecklinkIngestDlg::CreateDeviceMenu(void)   
  860. {   
  861.     HRESULT hr = S_OK;   
  862.     const GUID* pCategory = &CLSID_VideoInputDeviceCategory;   
  863.    
  864.     list< basic_string<wchar> > DeviceList;   
  865.     CDSUtils::EnumerateDevices(pCategory, DeviceList);   
  866.    
  867.     // Create a device menu item for each product present in the system.   
  868.     CMenu menuDevice;   
  869.     VERIFY(menuDevice.CreatePopupMenu());   
  870.     if (!DeviceList.empty())   
  871.     {   
  872.         int cDevices = 0;   
  873.         CComPtr<igraphbuilder> pGraph;   
  874.         if (SUCCEEDED(CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, __uuidof(IGraphBuilder), reinterpret_cast<void**>(&pGraph))))   
  875.         {   
  876.             for (list< basic_string<wchar> >::iterator it = DeviceList.begin(); it != DeviceList.end(); ++it)   
  877.             {   
  878.                 IBaseFilter* pFilter = NULL;   
  879.                 if (SUCCEEDED(CDSUtils::AddFilter2(pGraph, *pCategory, it->c_str(), &pFilter)))   
  880.                 {   
  881.                     WCHAR buf[256] = {0};   
  882.                     LPWSTR pVendorInfo = NULL;   
  883.    
  884.                     pFilter->QueryVendorInfo(&pVendorInfo);   
  885.                     if (pVendorInfo)   
  886.                     {   
  887.                         StringCbPrintfW(buf, sizeof(buf), L"%d. %s", cDevices + 1, pVendorInfo);   
  888.                         CoTaskMemFree(reinterpret_cast<lpvoid>(pVendorInfo));   
  889.                     }   
  890.                     else   
  891.                     {   
  892.                         StringCbPrintfW(buf, sizeof(buf), L"%d. %s", cDevices + 1, it->c_str());   
  893.                     }   
  894.    
  895.                     if (menuDevice.AppendMenu(MF_STRING, IDC_DEVICE_BASE + cDevices, CW2A(buf)))   
  896.                     {   
  897.                         MENUITEMINFO MenuItemInfo = {0};   
  898.                         MenuItemInfo.cbSize = sizeof(MenuItemInfo);   
  899.                         MenuItemInfo.fMask = MIIM_DATA;   
  900.    
  901.                         basic_string<wchar>::size_type len = it->length() + 1;   
  902.                         LPWSTR pName = new WCHAR[len];   
  903.                         ZeroMemory(pName, len);   
  904.                         StringCchCopyW(pName, len, it->c_str());   
  905.                            
  906.                         MenuItemInfo.dwItemData = (DWORD)(__int64)pName;   
  907.                         menuDevice.SetMenuItemInfo(IDC_DEVICE_BASE + cDevices, &MenuItemInfo);   
  908.                     }   
  909.    
  910.                     pGraph->RemoveFilter(pFilter);   
  911.                     SAFE_RELEASE(pFilter);   
  912.                        
  913.                     if (++cDevices >= CDecklinkIngestDlg::MAXDEVICES)   
  914.                     {   
  915.                         // Cannot support more than MAXDEVICES.   
  916.                         break;   
  917.                     }   
  918.                 }   
  919.             }   
  920.         }   
  921.    
  922.         if (0 < cDevices)   
  923.         {   
  924.             menuDevice.AppendMenu(MF_SEPARATOR, 0, TEXT("Separator"));   
  925.             menuDevice.AppendMenu(MF_STRING, IDC_DEVICE_INFO, TEXT("Device &Info.../tCtrl+I"));   
  926.                
  927.             if (m_Device >= cDevices)   
  928.             {   
  929.                 m_Device = 0;   
  930.                 EXECUTE_ASSERT(ERROR_SUCCESS == m_RegUtils.SetBinary(TEXT("VideoCaptureDevice"), reinterpret_cast<lpbyte>(&m_Device), sizeof(m_Device)));   
  931.             }   
  932.             menuDevice.CheckMenuRadioItem(IDC_DEVICE_BASE, IDC_DEVICE_BASE + CDecklinkIngestDlg::MAXDEVICES - 1, IDC_DEVICE_BASE + m_Device, MF_BYCOMMAND);   
  933.         }   
  934.         else   
  935.         {   
  936.             // It was not possible to instantiate any capture devices.   
  937.             menuDevice.AppendMenu(MF_STRING | MF_GRAYED, 0, TEXT("<no devices="">"));   
  938.             hr = S_FALSE;   
  939.         }   
  940.     }   
  941.     else   
  942.     {   
  943.         // No capture devices are present in the system.   
  944.         menuDevice.AppendMenu(MF_STRING | MF_GRAYED, 0, TEXT("<no devices="">"));   
  945.         hr = S_FALSE;   
  946.     }   
  947.    
  948.     // Insert the newly created device menu into the main menu.   
  949.     CMenu* pMenu = GetMenu();   
  950.     int Item = FindMenuItem(pMenu, TEXT("&Help"));   
  951.     if (-1 != Item)   
  952.     {   
  953.         hr = pMenu->InsertMenu(Item, MF_BYPOSITION | MF_POPUP | MF_STRING, (UINT_PTR)menuDevice.GetSafeHmenu(), TEXT("&Device")) ? S_OK : E_FAIL;   
  954.     }   
  955.     else   
  956.     {   
  957.         hr = E_FAIL;   
  958.     }   
  959.    
  960.     menuDevice.Detach();   
  961.    
  962.     return hr;   
  963. }   
  964.    
  965. //-----------------------------------------------------------------------------   
  966. // FindMenuItem   
  967. //   
  968. int CDecklinkIngestDlg::FindMenuItem(CMenu* pMenu, LPCTSTR MenuString)   
  969. {   
  970.     int Item = -1;   
  971.    
  972.     if (pMenu)   
  973.     {   
  974.         ASSERT(::IsMenu(pMenu->GetSafeHmenu()));   
  975.    
  976.         int Count = pMenu->GetMenuItemCount();   
  977.         for (int item=0; item<count; ++item)="" {="" cstring="" str;="" if="" (pmenu-="">GetMenuString(item, str, MF_BYPOSITION) && (strcmp(str, CString(MenuString)) == 0))   
  978.             {   
  979.                 Item = item;   
  980.                 break;   
  981.             }   
  982.         }   
  983.     }   
  984.    
  985.     return Item;   
  986. }   
  987.    
  988. //-----------------------------------------------------------------------------   
  989. // CreateThread   
  990. //   
  991. HRESULT CDecklinkIngestDlg::CreateThreads(void)   
  992. {   
  993.     HRESULT hr = S_OK;   
  994.    
  995.     if (NULL == m_hExitEvent)   
  996.     {   
  997.         m_hExitEvent = CreateEvent(NULL, FALSE, FALSE, NULL);   
  998.         if (m_hExitEvent)   
  999.         {   
  1000.             m_hThread = CreateThread(NULL, 0, ThreadProc, this, 0, NULL);   
  1001.             if (NULL == m_hThread)   
  1002.             {   
  1003.                 hr = AmGetLastErrorToHResult();   
  1004.             }   
  1005.         }   
  1006.         else   
  1007.         {   
  1008.             hr = AmGetLastErrorToHResult();   
  1009.         }   
  1010.     }   
  1011.     else   
  1012.     {   
  1013.         // Thread has already been created.   
  1014.         hr = S_FALSE;   
  1015.     }   
  1016.        
  1017.     return hr;   
  1018. }   
  1019.    
  1020. //-----------------------------------------------------------------------------   
  1021. // DestroyThread   
  1022. //   
  1023. HRESULT CDecklinkIngestDlg::DestroyThreads(void)   
  1024. {   
  1025.     HRESULT hr = S_OK;   
  1026.    
  1027.     if (m_hExitEvent)   
  1028.     {   
  1029.         // signal the thread to exit   
  1030.         SetEvent(m_hExitEvent);   
  1031.    
  1032.         // wait for thread to exit   
  1033.         EXECUTE_ASSERT(WAIT_OBJECT_0 == WaitForSingleObject(m_hThread, 10000));   
  1034.    
  1035.         CloseHandle(m_hExitEvent);   
  1036.         m_hExitEvent = NULL;   
  1037.            
  1038.         CloseHandle(m_hThread);   
  1039.         m_hThread = NULL;   
  1040.     }   
  1041.        
  1042.     return hr;   
  1043. }   
  1044.    
  1045. //-----------------------------------------------------------------------------   
  1046. // ThreadProc   
  1047. // Static function that wraps class thread implementation   
  1048. DWORD WINAPI CDecklinkIngestDlg::ThreadProc(LPVOID lpParameter)   
  1049. {   
  1050.     ASSERT(lpParameter);   
  1051.     CDecklinkIngestDlg* pDlg = reinterpret_cast<cdecklinkingestdlg*>(lpParameter);   
  1052.     return pDlg->Thread();   
  1053. }   
  1054.    
  1055. //-----------------------------------------------------------------------------   
  1056. // Thread   
  1057. // Actual class thread   
  1058. DWORD CDecklinkIngestDlg::Thread(void)   
  1059. {   
  1060.     HRESULT hr = S_OK;   
  1061.     enum { MODE_NEWCLIP, MODE_SEEKING_TO_PREROLL, MODE_PREROLLING, MODE_CAPTURING };   
  1062.     int State = MODE_NEWCLIP, CurrentItem, Preroll = 500;   
  1063.     CClip* pClip = NULL;   
  1064.    
  1065.     CTimecode Timecode;   
  1066.     REFERENCE_TIME rtTimecodeStart, rtTimecodeEnd;   
  1067.    
  1068.     static double LastRate = 0.0;   
  1069.    
  1070.     bool bRunning = true;   
  1071.     while (bRunning)   
  1072.     {   
  1073.         switch (WaitForSingleObject(m_hExitEvent, 5))   
  1074.         {   
  1075.             default:   
  1076.             case WAIT_ABANDONED:   
  1077.             case WAIT_OBJECT_0:   
  1078.                 bRunning = false;   
  1079.                 break;   
  1080.    
  1081.             case WAIT_TIMEOUT:   
  1082.                 // Periodically check the transport.   
  1083.                 CheckTransport();   
  1084.                 if (m_pIExtTransport)   
  1085.                 {   
  1086.                     basic_string<tchar> TimecodeSource;   
  1087.                     if (S_OK == m_pTimecodeGrabber->GetTimecode(Timecode, &rtTimecodeStart, &rtTimecodeEnd, &TimecodeSource))   
  1088.                     {   
  1089.                         UpdateTimecodeCtrl(Timecode);   
  1090.    
  1091.                         static basic_string<tchar> LastTimecodeSource;   
  1092.                         if (LastTimecodeSource != TimecodeSource)   
  1093.                         {   
  1094.                             VERIFY(m_StatusBarCtrl.SetText(TimecodeSource.c_str(), 1, 0));   
  1095.                             LastTimecodeSource = TimecodeSource;   
  1096.                         }   
  1097.    
  1098.                         if (m_bBatchCapture)   
  1099.                         {   
  1100.                             switch (State)   
  1101.                             {   
  1102.                                 case MODE_NEWCLIP:   
  1103.                                     // setup a new clip, if transport is available preroll to the inpoint   
  1104.                                     CurrentItem = m_ClipList.GetSelectedItem();   
  1105.                                     pClip = reinterpret_cast<cclip*>(m_ClipList.GetItemData(CurrentItem));   
  1106.                                     if (pClip)   
  1107.                                     {   
  1108.                                         // Build a sink side capturegraph and specify the stream start and stop times for frame accurate capture.   
  1109.                                         hr = CreateCaptureSinkGraph(pClip->Clipname().c_str());   
  1110.                                         if (SUCCEEDED(hr))   
  1111.                                         {   
  1112.                                             // update the inpoint and outpoint ctrls with the current clip   
  1113.                                             m_InpointCtrl.SetWindowText(pClip->Inpoint().TimecodeToString());   
  1114.                                             m_OutpointCtrl.SetWindowText(pClip->Outpoint().TimecodeToString());   
  1115.                                             m_DurationCtrl.SetWindowText(pClip->Duration().TimecodeToString());   
  1116.    
  1117.                                             if (m_CaptureTapename != pClip->Tapename())   
  1118.                                             {   
  1119.                                                 basic_string<tchar> Message = TEXT("Please check/load the tape ");   
  1120.                                                 Message.append(pClip->Tapename());   
  1121.                                                 Message.append(TEXT(" into the deck."));   
  1122.                                                 if (IDOK == MessageBox(Message.c_str(), TEXT("Check/Load tape"), MB_ICONEXCLAMATION | MB_OKCANCEL))   
  1123.                                                 {   
  1124.                                                     m_CaptureTapename = pClip->Tapename();   
  1125.                                                 }   
  1126.                                                 else   
  1127.                                                 {   
  1128.                                                     hr = E_FAIL;   
  1129.                                                 }   
  1130.                                             }   
  1131.                                         }   
  1132.                                            
  1133.                                         if (SUCCEEDED(hr))   
  1134.                                         {   
  1135.                                             LastRate = -1000.0;   
  1136.                                             State = MODE_SEEKING_TO_PREROLL;   
  1137.                                             VERIFY(m_StatusBarCtrl.SetText(TEXT("Seeking to inpoint..."), 0, 0));   
  1138.                                         }   
  1139.                                         else   
  1140.                                         {   
  1141.                                             // A serious problem occurred building the sink side capture graph.   
  1142.                                             OnBnClickedButtonAbort();   
  1143.                                         }   
  1144.                                     }   
  1145.                                     else   
  1146.                                     {   
  1147.                                         // No more clips.   
  1148.                                         OnBnClickedButtonAbort();   
  1149.                                     }   
  1150.                                     break;   
  1151.    
  1152.                                 case MODE_SEEKING_TO_PREROLL:   
  1153.                                     // At this point the transport is seeking to the preroll inpoint,   
  1154.                                     // wait for the deck to hit the preroll inpoint before rolling the transport.   
  1155.                                     {   
  1156.                                         // Calculate the 'distance' from the inpoint.   
  1157.                                         CTimecode PreRoll(pClip->Inpoint() - Preroll), Distance;   
  1158.                                         double Direction, Rate;   
  1159.    
  1160.                                         if (PreRoll > Timecode)   
  1161.                                         {   
  1162.                                             Distance = PreRoll - Timecode;   
  1163.                                             Direction = 1.0;    // Move transport forwards.   
  1164.                                         }   
  1165.                                         else   
  1166.                                         {   
  1167.                                             Distance = Timecode - PreRoll;   
  1168.                                             Direction = -1.0;   // Move transport backwards.   
  1169.                                         }   
  1170.    
  1171.                                         if (Distance < 200)  // 2 seconds from preroll.   
  1172.                                         {   
  1173.                                             // Move the transport at 1x.   
  1174.                                             Rate = Direction;   
  1175.                                         }   
  1176.                                         else if (Distance < 1000)    // 10 seconds from preroll.   
  1177.                                         {   
  1178.                                             // Move the transport at 10x.   
  1179.                                             Rate = Direction * 10.0;   
  1180.                                         }   
  1181.                                         else if (Distance < 3000)    // 30 seconds from preroll.   
  1182.                                         {   
  1183.                                             // Move the transport at 30x.   
  1184.                                             Rate = Direction * 30.0;   
  1185.                                         }   
  1186.                                         else    // 30+ seconds from preroll.   
  1187.                                         {   
  1188.                                             // Move the transport at 50x.   
  1189.                                             Rate = Direction * 50.0;   
  1190.                                         }   
  1191.    
  1192.                                         if (LastRate != Rate)   
  1193.                                         {   
  1194.                                             // Change the shuttle rate of the transport.   
  1195.                                             m_pIExtTransport->put_Rate(Rate);   
  1196.                                             m_pIExtTransport->put_Mode(ED_MODE_SHUTTLE);   
  1197.                                             LastRate = Rate;   
  1198.                                         }   
  1199.    
  1200.                                         if ((Distance == 0) && (1 == abs(Rate)))   
  1201.                                         {   
  1202.                                             // At the preroll inpoint, roll the transport and start prerolling.   
  1203.                                             m_pIExtTransport->put_Mode(ED_MODE_PLAY);   
  1204.                                             State = MODE_PREROLLING;   
  1205.                                             VERIFY(m_StatusBarCtrl.SetText(TEXT("Prerolling to inpoint..."), 0, 0));   
  1206.                                         }   
  1207.                                     }   
  1208.                                     break;   
  1209.                                    
  1210.                                 case MODE_PREROLLING:   
  1211.                                     // At this point the transport is rolling to the inpoint,   
  1212.                                     // determine the stream time of the inpoint and set the    
  1213.                                     // stream start times using IAMStreamControl.   
  1214.                                     {   
  1215.                                         CTimecode Distance = pClip->Inpoint() - Timecode;   
  1216.                                         if (MAXLONGLONG != rtTimecodeStart)   
  1217.                                         {   
  1218.                                             // The timecode grabber was able to provide a stream time for the current timecode.   
  1219.                                             // A short period before the inpoint, now that the deck is rolling, examine the stream   
  1220.                                             // time for the timecode sample and determine the stream time of the video frame that   
  1221.                                             // corresponds to the inpoint.   
  1222.                                             if (Distance == 10)   
  1223.                                             {   
  1224.                                                 REFERENCE_TIME rtAvgTimePerFrame = CUtils::GetAvgTimePerFrame(&m_VideoFormat);   
  1225.                                                 REFERENCE_TIME rtStart = rtTimecodeStart + (10 * rtAvgTimePerFrame);   
  1226.                                                 REFERENCE_TIME rtEnd = rtStart + ((pClip->Duration().GetFrameCount() - 1) * rtAvgTimePerFrame);   
  1227.                                                 ConfigureSourceGraph(&rtStart, &rtEnd); // Set the window of the stream times of samples that will cross the bridge.   
  1228.                                                 StartCapture(); // Close the bridge and unblock the capture streams.   
  1229.                                                 State = MODE_CAPTURING;   
  1230.                                                 TRACE(TEXT("CDecklinkIngestDlg::Thread() ***** INPOINT is [%10I64d  %10I64d] ***** %s  [%10I64d  %10I64d]/r/n"), rtStart, rtEnd, Timecode.TimecodeToString(), rtTimecodeStart, rtTimecodeEnd);   
  1231.                                             }   
  1232.                                         }   
  1233.                                         else   
  1234.                                         {   
  1235.                                             // The timecode grabber was not able to provide a stream time for the current timecode   
  1236.                                             // so wait for the inpoint before starting capture.  This is likely to suffer from   
  1237.                                             // inaccuracies.   
  1238.                                             if (Distance == 0)   
  1239.                                             {   
  1240.                                                 StartCapture(); // Close the bridge and unblock the capture streams.   
  1241.                                                 State = MODE_CAPTURING;   
  1242.                                                 TRACE(TEXT("CDecklinkIngestDlg::Thread() ***** INPOINT is NOW ***** %s/r/n"), Timecode.TimecodeToString());   
  1243.                                             }   
  1244.                                         }   
  1245.                                     }   
  1246.                                     break;   
  1247.                                        
  1248.                                 case MODE_CAPTURING:   
  1249.                                     // Wait for the sink side capture graph to issue an EC_STREAM_CONTROL_STARTED and   
  1250.                                     // EC_STREAM_CONTROL_STOPPED event.  What the latter is received continue with the next clip.   
  1251.                                     {   
  1252.                                         // Provide some metrics on the capture progess.   
  1253.                                         static CTimecode LastCaptured;   
  1254.                                         CTimecode Captured = Timecode - pClip->Inpoint();   
  1255.                                         CTimecode Remainder = pClip->Duration() - Captured;   
  1256.                                         if (LastCaptured != Captured)   
  1257.                                         {   
  1258.                                             LastCaptured = Captured;   
  1259.                                             basic_string<tchar> CaptureProgress = TEXT("Capturing...");   
  1260.                                             CaptureProgress.append(Captured.TimecodeToString());   
  1261.                                             CaptureProgress.append(TEXT("                                                    -"));   
  1262.                                             CaptureProgress.append(Remainder.TimecodeToString());   
  1263.                                             VERIFY(m_StatusBarCtrl.SetText(CaptureProgress.c_str(), 0, 0));   
  1264.                                             m_ProgressCtrl.ShowWindow(SW_SHOWNORMAL);   // Show the progress control.   
  1265.                                             m_ProgressCtrl.SetPos(Captured.GetFrameCount() * 100 / pClip->Duration().GetFrameCount());   
  1266.                                         }   
  1267.    
  1268.                                         long lEventCode, lParam1, lParam2;   
  1269.                                         HRESULT hr = S_OK;   
  1270.                                         while (hr = m_pIMediaEvent->GetEvent(&lEventCode, &lParam1, &lParam2, 0), SUCCEEDED(hr))   
  1271.                                         {   
  1272.                                             hr = m_pIMediaEvent->FreeEventParams(lEventCode, lParam1, lParam2);   
  1273.    
  1274.                                             if (EC_STREAM_CONTROL_STARTED == lEventCode)   
  1275.                                             {   
  1276.                                                 TRACE(TEXT("CDecklinkIngestDlg::Thread() - Stream started event/r/n"));   
  1277.                                             }   
  1278.                                             else if (EC_STREAM_CONTROL_STOPPED == lEventCode)   
  1279.                                             {   
  1280.                                                 TRACE(TEXT("CDecklinkIngestDlg::Thread() - Stream stopped event/r/n"));   
  1281.                                                 m_pIExtTransport->put_Mode(ED_MODE_STOP);    // Stop the transport.   
  1282.    
  1283.                                                 if (0 == m_ClipList.SetSelectedItem(++CurrentItem))   
  1284.                                                 {   
  1285.                                                     // No more clips.   
  1286.                                                     OnBnClickedButtonAbort();   
  1287.                                                     MessageBox(TEXT("Batch capture completed."), TEXT("Batch capture"), MB_ICONINFORMATION | MB_OK);   
  1288.                                                 }   
  1289.                                                 else   
  1290.                                                 {   
  1291.                                                     StopCapture();  // Open the bridge and block the capture streams.   
  1292.                                                     m_ProgressCtrl.ShowWindow(SW_HIDE); // Hide the progress control.   
  1293.                                                 }   
  1294.    
  1295.                                                 State = MODE_NEWCLIP;   
  1296.                                                 break;   
  1297.                                             }   
  1298.                                         }   
  1299.                                     }   
  1300.                                     break;   
  1301.                             }   
  1302.                         }   
  1303.                         else   
  1304.                         {   
  1305.                             if (MODE_NEWCLIP != State)   
  1306.                             {   
  1307.                                 State = MODE_NEWCLIP;   
  1308.                             }   
  1309.                         }   
  1310.                     }   
  1311.                     else if (m_bBatchCapture)   
  1312.                     {   
  1313.                         // If failed to get timecode in the middle of a batch capture, abort.   
  1314.                         OnBnClickedButtonAbort();   
  1315.                         State = MODE_NEWCLIP;   
  1316.                     }   
  1317.                 }   
  1318.                 break;         
  1319.         }      
  1320.     }   
  1321.    
  1322.     return 0;   
  1323. }   
  1324.    
  1325. //-----------------------------------------------------------------------------   
  1326. // CheckTransport   
  1327. // Make some rudimentary checks of the transport, such as are there comms (is   
  1328. // the RS232 connected), is it in remote mode, is the tape loaded and is record   
  1329. // inhibit disabled.   
  1330. HRESULT CDecklinkIngestDlg::CheckTransport(void)   
  1331. {   
  1332.     HRESULT hr = S_FALSE;   
  1333.    
  1334.     static int bLastEnableTimecodeCtrl = -1;   
  1335.     static int bLastEnableTransportCtrls = -1;   
  1336.     int bEnableTimecodeCtrl = FALSE;   
  1337.     int bEnableTransportCtrls = FALSE;   
  1338.    
  1339.     if (m_pIExtTransport)   
  1340.     {   
  1341.         // Test to see if there are comms with the deck.   
  1342.         long state;   
  1343.         hr = m_pIExtTransport->get_LocalControl(&state);   
  1344.         if (SUCCEEDED(hr))   
  1345.         {   
  1346.             if (OAFALSE == state)   
  1347.             {   
  1348.                 // The deck is in remote control mode.   
  1349.                 hr = m_pIExtTransport->get_MediaState(&state);   
  1350.                 if (SUCCEEDED(hr) && (ED_MEDIA_UNLOAD != state))   
  1351.                 {   
  1352.                     // Enable the controls as there are comms with the deck,   
  1353.                     // it is in remote mode and there is a tape loaded.   
  1354.                     bEnableTimecodeCtrl = bEnableTransportCtrls = TRUE;   
  1355.                     hr = S_OK;   
  1356.                 }   
  1357.             }   
  1358.         }   
  1359.     }   
  1360.     else   
  1361.     {   
  1362.         hr = E_FAIL;   
  1363.     }   
  1364.    
  1365.     CWnd* pWnd = NULL;   
  1366.     if (bLastEnableTimecodeCtrl != bEnableTimecodeCtrl)   
  1367.     {   
  1368.         pWnd = GetDlgItem(IDC_STATIC_TIMECODE);   
  1369.         pWnd->EnableWindow(bEnableTimecodeCtrl ? TRUE : FALSE);   
  1370.         bLastEnableTimecodeCtrl = bEnableTimecodeCtrl;   
  1371.     }   
  1372.    
  1373.     if (bLastEnableTransportCtrls != bEnableTransportCtrls)   
  1374.     {   
  1375.         EnableWindows(TransportCtrlIDs, SIZEOF_ARRAY(TransportCtrlIDs), bEnableTransportCtrls ? TRUE : FALSE);   
  1376.         bLastEnableTransportCtrls = bEnableTransportCtrls;   
  1377.     }   
  1378.    
  1379.     return hr;   
  1380. }   
  1381.    
  1382. //-----------------------------------------------------------------------------   
  1383. // UpdateTimecodeCtrl   
  1384. //   
  1385. void CDecklinkIngestDlg::UpdateTimecodeCtrl(CTimecode& Timecode)   
  1386. {   
  1387.     static CTimecode LastTimecode;   
  1388.    
  1389.     if (LastTimecode != Timecode)   
  1390.     {   
  1391.         DWORD dwStyle = m_TimecodeCtrl.GetStyle();   
  1392.         if (!(dwStyle & WS_DISABLED))   
  1393.         {   
  1394.             m_TimecodeCtrl.SetWindowText(Timecode.TimecodeToString());   
  1395.             LastTimecode = Timecode;   
  1396.         }   
  1397.     }   
  1398. }   
  1399.    
  1400. //-----------------------------------------------------------------------------   
  1401. // CreateCaptureSourceGraph   
  1402. // Build a full source side capture graph but with the capture path diabled.  This   
  1403. // will preview the capture stream until a capture option is selected.  The capture   
  1404. // stream will then be enabled in order to write to disk.   
  1405. //   
  1406. // The following source side graph is built:   
  1407. //   
  1408. // Decklink Video Capture (Capture) -> Smart T (Capture) -> (Input 1) GMFBridge Sink   
  1409. //                                     Smart T (Preview) -> AVI Decompressor -> Stills Sample Grabber -> Video Renderer   
  1410. // Decklink Video Capture (~Timecode) -> Timecode Sample Grabber -> Null Renderer   
  1411. // Decklink Audio Capture (Capture) -> Smart T (Capture) -> (Input 2) GMFBridge Sink   
  1412. //   
  1413. //   
  1414. // TODO: HDV Capture, device is 'Microsoft AV/C Tape Subunit Device' with output pin 'MPEG2TS Out'   
  1415. //       DV Capture, device is 'Microsoft DV Camera and VCR' with output pins 'DV Vid Out' and 'DV A/V Out'   
  1416. //       NOTE: These devices can come and go so it will be necessary to keep checking that the device is available.   
  1417. //   
  1418. HRESULT CDecklinkIngestDlg::CreateCaptureSourceGraph(void)   
  1419. {   
  1420.     DestroyThreads();   // Destroy the thread which uses the source interfaces that are about to be released.   
  1421.     DestroySinkGraph();   
  1422.     DestroySourceGraph();   
  1423.    
  1424.     HRESULT hr = CreateThreads(), hr2 = S_OK;   
  1425.     if (SUCCEEDED(hr))   
  1426.     {   
  1427.         // Create a bridge controller object.   
  1428.         hr = CoCreateInstance(__uuidof(GMFBridgeController), NULL, CLSCTX_INPROC_SERVER, __uuidof(IGMFBridgeController), reinterpret_cast<void**>(&m_pIGMFBridgeController));   
  1429.         if (SUCCEEDED(hr))   
  1430.         {   
  1431.             // Add two streams to the bridge controller, one for audio and one for video.   
  1432.             hr = m_pIGMFBridgeController->AddStream(TRUE, eMuxInputs, TRUE);   
  1433.                
  1434.             if (false == m_bMuteAudio)   
  1435.             {   
  1436.                 hr2 = m_pIGMFBridgeController->AddStream(FALSE, eMuxInputs, TRUE);   
  1437.             }   
  1438.    
  1439.             if (SUCCEEDED(hr) && SUCCEEDED(hr2))   
  1440.             {   
  1441.                 // Add a bridge sink filter for the audio and the video streams.   
  1442.                 CComPtr<iunknown> pIUnknown;   
  1443.                 hr = m_pIGMFBridgeController->InsertSinkFilter(m_pSourceGraph, &pIUnknown);   
  1444.                 if (SUCCEEDED(hr))   
  1445.                 {   
  1446.                     CMediaType MediaType;   
  1447.                     MediaType.InitMediaType();   
  1448.    
  1449.                     m_pBridgeSink = pIUnknown;   
  1450.    
  1451.                     // Add the video capture filter.   
  1452.                     hr = CDSUtils::AddFilter2(m_pSourceGraph, CLSID_VideoInputDeviceCategory, m_pDeviceNameVideo, &m_pVideoDevice);   
  1453.                     if (SUCCEEDED(hr))   
  1454.                     {   
  1455.                         m_pIExtTransport = m_pVideoDevice;  // QI capture source for the transport control interface.   
  1456.                         m_pTimecodeGrabber->SetTimecodeFilter(m_pVideoDevice);   // Set the filter for reading timecode from VITC, RP188 and RS422   
  1457.                            
  1458.                         // Set the video format.   
  1459.                         CComPtr<iamstreamconfig> pIAMStreamConfig;   
  1460.                         hr = CDSUtils::FindPinInterface(m_pVideoDevice, L"Capture", __uuidof(IAMStreamConfig), reinterpret_cast<void**>(&pIAMStreamConfig));   
  1461.                         if (SUCCEEDED(hr))   
  1462.                         {   
  1463.                             hr = pIAMStreamConfig->SetFormat(&m_VideoFormat);   
  1464.                         }   
  1465.                            
  1466.                         // Set the timecode format.   
  1467.                         pIAMStreamConfig = NULL;   
  1468.                         hr = CDSUtils::FindPinInterface(m_pVideoDevice, L"~Timecode", __uuidof(IAMStreamConfig), reinterpret_cast<void**>(&pIAMStreamConfig));   
  1469.                         if (SUCCEEDED(hr))   
  1470.                         {   
  1471.                             MediaType.SetType(&MEDIATYPE_Timecode);   
  1472.                             MediaType.SetSampleSize(sizeof(TIMECODE_SAMPLE));   
  1473.                             MediaType.SetFormatType(&FORMAT_None);   
  1474.                             hr = pIAMStreamConfig->SetFormat(&MediaType);   
  1475.                         }   
  1476.                     }   
  1477.                            
  1478.                     // Add the audio capture filter by creating the audio capture filter name from the name of the video capture filter.   
  1479.                     basic_string<wchar> DeviceNameAudio = m_pDeviceNameVideo;   
  1480.                     basic_string<wchar>::size_type Index = DeviceNameAudio.find(L"Video");   
  1481.                     if (basic_string<wchar>::npos != Index)   
  1482.                     {   
  1483.                         DeviceNameAudio.replace(Index, 5, L"Audio");   
  1484.                     }   
  1485.                     hr = CDSUtils::AddFilter2(m_pSourceGraph, CLSID_AudioInputDeviceCategory, DeviceNameAudio.c_str(), &m_pAudioDevice);   
  1486.                     if (SUCCEEDED(hr))   
  1487.                     {   
  1488.                         // Set the audio format.   
  1489.                         CComPtr<iamstreamconfig> pIAMStreamConfig;   
  1490.                         hr = CDSUtils::FindPinInterface(m_pAudioDevice, L"Capture", __uuidof(IAMStreamConfig), reinterpret_cast<void**>(&pIAMStreamConfig));   
  1491.                         if (SUCCEEDED(hr))   
  1492.                         {   
  1493.                             hr = pIAMStreamConfig->SetFormat(&m_AudioFormat);   
  1494.                         }   
  1495.                     }   
  1496.    
  1497.                     if (SUCCEEDED(hr))   
  1498.                     {   
  1499.                         // Attempt to render the timecode pin.   
  1500.                         hr = CDSUtils::ConnectFilters(m_pSourceGraph, m_pVideoDevice, L"~Timecode", m_pSampleGrabberTimecode, NULL);   
  1501.                         if (SUCCEEDED(hr))   
  1502.                         {   
  1503.                             hr = CDSUtils::ConnectFilters(m_pSourceGraph, m_pSampleGrabberTimecode, NULL, m_pNullRenderer, NULL);   
  1504.                         }   
  1505.    
  1506.                         // Attempt to render the video preview path.   
  1507.                         hr = CDSUtils::ConnectFilters(m_pSourceGraph, m_pVideoDevice, L"Capture", m_pSmartT, NULL);   
  1508.                         if (SUCCEEDED(hr))   
  1509.                         {   
  1510.                             // Set the media type for the still sample grabber by modifying a copy of the video capture format.   
  1511.                             MediaType.Set(m_VideoFormat);   // Copy the current video format.   
  1512.                             MediaType.SetSubtype(&MEDIASUBTYPE_RGB32);   
  1513.                             BITMAPINFOHEADER* pbmih = CUtils::GetBMIHeader(&MediaType);   
  1514.                             if (pbmih)   
  1515.                             {   
  1516.                                 // Set to BGRA as the still grabber can only handles this pixel format.   
  1517.                                 pbmih->biBitCount = 32;   
  1518.                                 pbmih->biCompression = BI_RGB;   
  1519.                                 pbmih->biSizeImage = CUtils::GetImageSize(pbmih);   
  1520.                                 MediaType.SetSampleSize(pbmih->biSizeImage);   
  1521.    
  1522.                                 m_pStillGrabber->SetBMIH(pbmih);   
  1523.                                 if (m_pISampleGrabberStills)   
  1524.                                 {   
  1525.                                     hr = m_pISampleGrabberStills->SetMediaType(&MediaType);   
  1526.                                 }   
  1527.                             }   
  1528.    
  1529.                             // Connect the preview pin of the smart-T to the video renderer.   
  1530.                             hr = CDSUtils::ConnectFilters(m_pSourceGraph, m_pSmartT, L"Preview", m_pSampleGrabberStills, NULL);   
  1531.                             if (SUCCEEDED(hr))   
  1532.                             {   
  1533.                                 hr = CDSUtils::ConnectFilters(m_pSourceGraph, m_pSampleGrabberStills, NULL, m_pVideoRenderer, NULL);   
  1534.                                 if (SUCCEEDED(hr))   
  1535.                                 {   
  1536.                                     EnableSampleGrabberCallback(true);   
  1537.                                 }   
  1538.                             }   
  1539.                         }   
  1540.    
  1541.                         // Attempt to render the video capture path.   
  1542.                         if (SUCCEEDED(hr))   
  1543.                         {   
  1544.                             hr = CDSUtils::ConnectFilters(m_pSourceGraph, m_pSmartT, L"Capture", m_pBridgeSink, L"Input 1");   
  1545.                         }   
  1546.    
  1547.                         // Attempt to render the audio path.   
  1548.                         if (SUCCEEDED(hr) && (false == m_bMuteAudio))   
  1549.                         {   
  1550.                             hr = CDSUtils::ConnectFilters(m_pSourceGraph, m_pAudioDevice, L"Capture", m_pBridgeSink, NULL);   
  1551.                         }   
  1552.    
  1553.                         // Run the source side capture graph until a change is made that requires the graph   
  1554.                         // to be rebuilt, such as a different capture device is selected.   
  1555.                         if (SUCCEEDED(hr))   
  1556.                         {   
  1557.                             // Turn off the capture streams.   
  1558.                             hr = ConfigureSourceGraph(NULL, NULL);   
  1559.    
  1560.                             CComQIPtr<imediacontrol, &iid_imediacontrol=""> pIMediaControl = m_pSourceGraph;   
  1561.                             if (pIMediaControl)   
  1562.                             {   
  1563.                                 pIMediaControl->Run();   
  1564.                             }   
  1565.                         }   
  1566.                     }   
  1567.                 }   
  1568.             }   
  1569.         }   
  1570.     }   
  1571.    
  1572.     return hr;   
  1573. }   
  1574.    
  1575. //-----------------------------------------------------------------------------   
  1576. // ConfigureSourceGraph   
  1577. // Sets the stream start and stop times on the input pins of the bridge sink filter.   
  1578. // As all pins are configured to have the same start and stop times, only one pin is   
  1579. // configured to issue EC_STREAM_CONTROL_STARTED and EC_STREAM_CONTROL_STOPPED event   
  1580. // notifications.  These notifications are useful for monitoring the state of the   
  1581. // stream and once EC_STREAM_CONTROL_STOPPED is received, the file writing graph   
  1582. // can be stopped to finalise the file and complete the capture.   
  1583. HRESULT CDecklinkIngestDlg::ConfigureSourceGraph(REFERENCE_TIME* prtStart, REFERENCE_TIME* prtStop)   
  1584. {   
  1585.     HRESULT hr = S_OK;   
  1586.    
  1587.     if (m_pBridgeSink)   
  1588.     {   
  1589.         DWORD dwStartCookie = 0x100, dwStopCookie = 0x10000;   
  1590.    
  1591.         // Process all the connected input pins of bridge source filter.   
  1592.         CComPtr<ienumpins> pIEnumPins = NULL;   
  1593.         hr = m_pBridgeSink->EnumPins(&pIEnumPins);   
  1594.         if (SUCCEEDED(hr))   
  1595.         {   
  1596.             CComQIPtr<iamstreamcontrol, &iid_iamstreamcontrol=""> pIAMStreamControl;   
  1597.    
  1598.             IPin* pIPin = NULL;   
  1599.             while ((S_OK == pIEnumPins->Next(1, &pIPin, NULL)) && SUCCEEDED(hr))   
  1600.             {   
  1601.                 // Determine if the pin is connected.   
  1602.                 IPin* pIPinTemp = NULL;   
  1603.                 if (SUCCEEDED(pIPin->ConnectedTo(&pIPinTemp)))   
  1604.                 {   
  1605.                     SAFE_RELEASE(pIPinTemp);    // Release the outstanding reference.   
  1606.    
  1607.                     // Set the start and stop times on connected input pins and fail   
  1608.                     // if the connected input pin does not have a stream control interface.   
  1609.                     pIAMStreamControl = pIPin;   
  1610.                     if (pIAMStreamControl)   
  1611.                     {   
  1612.                         // Configure the stream with the given start and stop times.   
  1613.                         hr = pIAMStreamControl->StartAt(prtStart, dwStartCookie);   
  1614.                         if (SUCCEEDED(hr))   
  1615.                         {   
  1616.                             hr = pIAMStreamControl->StopAt(prtStop, FALSE, dwStopCookie);   
  1617.                         }   
  1618.                            
  1619.                         // As all the streams will start and stop at the same time only one pin   
  1620.                         // needs to issue the EC_STREAM_CONTROL_STARTED and EC_STREAM_CONTROL_STOPPED   
  1621.                         // event notifications.   
  1622.                         dwStartCookie = dwStopCookie = 0;   
  1623.                     }   
  1624.                     else   
  1625.                     {   
  1626.                         hr = E_NOINTERFACE;   
  1627.                     }   
  1628.                 }   
  1629.    
  1630.                 SAFE_RELEASE(pIPin);   
  1631.             }   
  1632.         }   
  1633.     }   
  1634.     else   
  1635.     {   
  1636.         hr = E_POINTER;   
  1637.     }   
  1638.        
  1639.     return hr;   
  1640. }   
  1641.    
  1642. //-----------------------------------------------------------------------------   
  1643. // CreateCaptureSinkGraph   
  1644. // Create a sink side graph that will use the selected compression method before   
  1645. // writing the streams to disk.  A new graph is created for each file and is independent   
  1646. // of the source side graph until the bridge is closed.  This graph is not run until   
  1647. // capture has been initiated.   
  1648. HRESULT CDecklinkIngestDlg::CreateCaptureSinkGraph(LPCTSTR pszFilename)   
  1649. {   
  1650.     HRESULT hr = S_OK;   
  1651.        
  1652.     if (pszFilename)   
  1653.     {   
  1654.         hr = DestroySinkGraph();   
  1655.         if (SUCCEEDED(hr))   
  1656.         {   
  1657.             // Add a bridge source filter.   
  1658.             CComPtr<iunknown> pIUnknown;   
  1659.             hr = m_pIGMFBridgeController->InsertSourceFilter(m_pBridgeSink, m_pSinkGraph, &pIUnknown);   
  1660.             if (SUCCEEDED(hr))   
  1661.             {   
  1662.                 m_pBridgeSource = pIUnknown;   
  1663.    
  1664.                 // From the compression type, add a mux and file writer where appropriate.   
  1665.                 CComPtr<ibasefilter> pAVIMux, pFileWriter, pStreamControlFilter;   
  1666.                 if (ENC_WM == m_Compression)   
  1667.                 {   
  1668.                     hr = CDSUtils::AddFilter(m_pSinkGraph, CLSID_WMAsfWriter, L"WM ASF Writer", &pFileWriter);   
  1669.                     pStreamControlFilter = pFileWriter;   
  1670.                 }   
  1671.                 else if ((ENC_SEQ_CIN == m_Compression) || (ENC_SEQ_DPX == m_Compression) || (ENC_SEQ_TGA == m_Compression) || (ENC_SEQ_BMP == m_Compression))   
  1672.                 {   
  1673.                     hr = CDSUtils::AddFilter(m_pSinkGraph, CLSID_DecklinkStillSequenceSinkFilter, L"Decklink Still Sequence Sink", &pFileWriter);   
  1674.                     pStreamControlFilter = pFileWriter;   
  1675.                 }   
  1676.                 else   
  1677.                 {   
  1678.                     hr = CDSUtils::AddFilter(m_pSinkGraph, CLSID_AviDest, L"AVI Mux", &pAVIMux);   
  1679.                     if (SUCCEEDED(hr))   
  1680.                     {   
  1681.                         hr = CDSUtils::AddFilter(m_pSinkGraph, CLSID_FileWriter, L"File Writer", &pFileWriter);   
  1682.                     }   
  1683.                     pStreamControlFilter = pAVIMux;   
  1684.                 }   
  1685.    
  1686.                 // Build a file writing graph using the appropriate compressor.   
  1687.                 if (SUCCEEDED(hr))   
  1688.                 {   
  1689.                     // Obtain the IFileSinkFilter interface from the file writer filter and set the capture filename.   
  1690.                     m_pIFileSinkFilter = pFileWriter;   
  1691.                     if (m_pIFileSinkFilter)   
  1692.                     {   
  1693.                         // Create the full path of the capture filename.   
  1694.                         TCHAR szCaptureFilePath[MAX_PATH] = {0};   
  1695.                         StringCchPrintf(szCaptureFilePath, MAX_PATH, TEXT("%s"), m_CaptureFilePath.c_str());   
  1696.                         PathAppend(szCaptureFilePath, pszFilename);   
  1697.                         StringCchCat(szCaptureFilePath, MAX_PATH, m_CaptureFileExtension.c_str());   
  1698.    
  1699.                         hr = m_pIFileSinkFilter->SetFileName(CT2OLE(szCaptureFilePath), NULL);   
  1700.    
  1701.                         if (SUCCEEDED(hr))   
  1702.                         {   
  1703.                             switch (m_Compression)   
  1704.                             {   
  1705.                                 default:   
  1706.                                 case ENC_NONE:   
  1707.                                     hr = CreateUncompressedCaptureSinkGraph(pAVIMux, pFileWriter);   
  1708.                                     break;   
  1709.                                        
  1710.                                 case ENC_DV:   
  1711.                                     hr = CreateDVCaptureSinkGraph(pAVIMux, pFileWriter);   
  1712.                                     break;   
  1713.                                        
  1714.                                 case ENC_WM:   
  1715.                                     hr = CreateWMCaptureSinkGraph(NULL, pFileWriter);   
  1716.                                     break;   
  1717.                                        
  1718.                                 case ENC_MJ:   
  1719.                                     hr = CreateMJPEGCaptureSinkGraph(pAVIMux, pFileWriter);   
  1720.                                     break;   
  1721.    
  1722.                                 case ENC_SEQ_CIN:   
  1723.                                 case ENC_SEQ_DPX:   
  1724.                                 case ENC_SEQ_TGA:   
  1725.                                 case ENC_SEQ_BMP:   
  1726.                                     hr = CreateStillSequenceCaptureSinkGraph(NULL, pFileWriter);   
  1727.                                     break;   
  1728.                             }   
  1729.    
  1730.                             if (SUCCEEDED(hr))   
  1731.                             {   
  1732.                                 CComQIPtr<imediacontrol, &iid_imediacontrol=""> pIMediaControl = m_pSinkGraph;   
  1733.                                 if (pIMediaControl)   
  1734.                                 {   
  1735.                                     pIMediaControl->Pause();   
  1736.                                 }   
  1737.                             }   
  1738.                         }   
  1739.                     }   
  1740.                     else   
  1741.                     {   
  1742.                         hr = E_POINTER;   
  1743.                     }   
  1744.                 }   
  1745.             }   
  1746.         }   
  1747.     }   
  1748.     else   
  1749.     {   
  1750.         hr = E_INVALIDARG;   
  1751.     }   
  1752.    
  1753.     if (FAILED(hr))   
  1754.     {   
  1755.         MessageBox(TEXT("An error was detected while building/r/nthe compression and file writing graph."), TEXT("DirectShow Error"), MB_OK | MB_ICONEXCLAMATION);   
  1756.     }   
  1757.    
  1758.     return hr;   
  1759. }   
  1760.    
  1761. //-----------------------------------------------------------------------------   
  1762. // CreateUncompressedCaptureSinkGraph   
  1763. // Create a graph for writing uncompressed streams to disk using the AVI mux   
  1764. // and file writer filters.   
  1765. //   
  1766. // The following sink side graph is built:   
  1767. //   
  1768. // GMFBridge Source (Output 1) -> (Input 01) AVI Mux -> File Writer   
  1769. // GMFBridge Source (Output 2) -> (Input 02) AVI Mux   
  1770. //   
  1771. HRESULT CDecklinkIngestDlg::CreateUncompressedCaptureSinkGraph(IBaseFilter* pAVIMux, IBaseFilter* pFileWriter)   
  1772. {   
  1773.     HRESULT hr = S_OK;   
  1774.    
  1775.     if (pAVIMux && pFileWriter)   
  1776.     {   
  1777.         // Connect the bridge video output pin to the mux.   
  1778.         hr = CDSUtils::ConnectFilters(m_pSinkGraph, m_pBridgeSource, L"Output 1", pAVIMux, NULL);   
  1779.         if (SUCCEEDED(hr))   
  1780.         {   
  1781.             // Connect the mux to the file writer.   
  1782.             hr = CDSUtils::ConnectFilters(m_pSinkGraph, pAVIMux, NULL, pFileWriter, NULL);   
  1783.    
  1784.             // Video path connected now optionally connect the audio path.   
  1785.             if (false == m_bMuteAudio)   
  1786.             {   
  1787.                 // Connect the bridge audio output pin to the mux.   
  1788.                 hr = CDSUtils::ConnectFilters(m_pSinkGraph, m_pBridgeSource, L"Output 2", pAVIMux, NULL);   
  1789.             }   
  1790.         }   
  1791.     }   
  1792.     else   
  1793.     {   
  1794.         hr = E_POINTER;   
  1795.     }   
  1796.    
  1797.     return hr;   
  1798. }   
  1799.    
  1800. //-----------------------------------------------------------------------------   
  1801. // CreateDVCaptureSinkGraph   
  1802. // Create a graph for writing DV compressed streams to disk using the AVI mux   
  1803. // and file writer filter.  A custom field swap filter is inserted for PAL   
  1804. //   
  1805. // The following sink side graph is built:   
  1806. //   
  1807. // GMFBridge Source (Output 1) -> AVI Decompressor -> DV Video Encoder -> (Input 01) AVI Mux -> File Writer   
  1808. // GMFBridge Source (Output 2) -----------------------------------------> (Input 02) AVI Mux   
  1809. //   
  1810. HRESULT CDecklinkIngestDlg::CreateDVCaptureSinkGraph(IBaseFilter* pAVIMux, IBaseFilter* pFileWriter)   
  1811. {   
  1812.     HRESULT hr = S_OK;   
  1813.     BITMAPINFOHEADER* pbmih = CUtils::GetBMIHeader(m_VideoFormat);  // This is required to configure the DV encoder.   
  1814.    
  1815.     if (pAVIMux && pFileWriter && pbmih)   
  1816.     {   
  1817.         // Locate the DV encoder and add it to the graph.   
  1818.         CComPtr<ibasefilter> pEncoder = NULL;   
  1819.         HRESULT hr = CDSUtils::AddFilter(m_pSinkGraph, CLSID_DVVideoEnc, L"DV Video Encoder", &pEncoder);   
  1820.         if (SUCCEEDED(hr))   
  1821.         {   
  1822.             // Configure the DV encoder.   
  1823.             CComQIPtr<idvenc, &iid_idvenc=""> pIDV = pEncoder;   
  1824.             if (pIDV)   
  1825.             {   
  1826.                 // Located a DV compression filter so set the format.   
  1827.                 int VideoFormat, DVFormat, Resolution;   
  1828.                 hr = pIDV->get_IFormatResolution(&VideoFormat, &DVFormat, &Resolution, FALSE, NULL);   
  1829.                 if (SUCCEEDED(hr))   
  1830.                 {   
  1831.                     ASSERT(DVENCODERFORMAT_DVSD == DVFormat);   
  1832.                     ASSERT(DVENCODERRESOLUTION_720x480 == Resolution);   
  1833.    
  1834.                     if ((DVENCODERVIDEOFORMAT_NTSC == VideoFormat) && (576 == pbmih->biHeight))   
  1835.                     {   
  1836.                         // Set the encoder to PAL if it is currently NTSC.   
  1837.                         VideoFormat = DVENCODERVIDEOFORMAT_PAL;   
  1838.                         hr = pIDV->put_IFormatResolution(VideoFormat, DVFormat, Resolution, FALSE, NULL);   
  1839.                     }   
  1840.                     else if ((DVENCODERVIDEOFORMAT_PAL == VideoFormat) && (486 == pbmih->biHeight))   
  1841.                     {   
  1842.                         // Set the encoder to NTSC if it is currently PAL.   
  1843.                         VideoFormat = DVENCODERVIDEOFORMAT_NTSC;   
  1844.                         hr = pIDV->put_IFormatResolution(VideoFormat, DVFormat, Resolution, FALSE, NULL);   
  1845.                     }   
  1846.                 }   
  1847.             }   
  1848.    
  1849.             if (SUCCEEDED(hr))   
  1850.             {   
  1851.                 // If the format is PAL, insert the Decklink field swap filter, PAL DV is the opposite field order to PAL SD.   
  1852.                 if (576 == pbmih->biHeight)   
  1853.                 {   
  1854.                     CComPtr<ibasefilter> pPALFieldSwap = NULL;   
  1855.                     hr = CDSUtils::AddFilter(m_pSinkGraph, CLSID_DecklinkFieldSwap, L"Decklink PAL Field Swap", &pPALFieldSwap);   
  1856.                     if (SUCCEEDED(hr))   
  1857.                     {   
  1858.                         // Connect the bridge video output pin to the PAL field swap filter.   
  1859.                         hr = CDSUtils::ConnectFilters(m_pSinkGraph, m_pBridgeSource, L"Output 1", pPALFieldSwap, NULL);   
  1860.                         if (SUCCEEDED(hr))   
  1861.                         {   
  1862.                             // Connect the field swap filter to the DV encoder.   
  1863.                             hr = CDSUtils::ConnectFilters(m_pSinkGraph, pPALFieldSwap, NULL, pEncoder, NULL);   
  1864.                         }   
  1865.                     }   
  1866.                 }   
  1867.                 else   
  1868.                 {   
  1869.                     // Connect the bridge video output pin to the DV Encoder.   
  1870.                     hr = CDSUtils::ConnectFilters(m_pSinkGraph, m_pBridgeSource, L"Output 1", pEncoder, NULL);   
  1871.                 }   
  1872.    
  1873.                 if (SUCCEEDED(hr))   
  1874.                 {   
  1875.                     // Connect the DV encoder output to the AVI mux.   
  1876.                     hr = CDSUtils::ConnectFilters(m_pSinkGraph, pEncoder, NULL, pAVIMux, NULL);   
  1877.                     if (SUCCEEDED(hr))   
  1878.                     {   
  1879.                         // Connect the mux to the file writer.   
  1880.                         hr = CDSUtils::ConnectFilters(m_pSinkGraph, pAVIMux, NULL, pFileWriter, NULL);   
  1881.                         if (SUCCEEDED(hr))   
  1882.                         {   
  1883.                             // Video path connected now optionally connect the audio path.   
  1884.                             if (false == m_bMuteAudio)   
  1885.                             {   
  1886.                                 // Connect the bridge audio output pin to the mux.   
  1887.                                 hr = CDSUtils::ConnectFilters(m_pSinkGraph, m_pBridgeSource, L"Output 2", pAVIMux, NULL);   
  1888.                             }   
  1889.                         }   
  1890.                     }   
  1891.                 }   
  1892.             }   
  1893.         }   
  1894.     }   
  1895.     else   
  1896.     {   
  1897.         hr = E_POINTER;   
  1898.     }   
  1899.    
  1900.     return hr;   
  1901. }   
  1902.    
  1903. //-----------------------------------------------------------------------------   
  1904. // CreateWMCaptureSinkGraph   
  1905. // Create a graph for writing WM compressed streams to disk using the WM ASF writer   
  1906. // filter.   
  1907. //   
  1908. // The following sink side graph is built:   
  1909. //   
  1910. // GMFBridge Source (Output 1) -> (Video Input 1) WM ASF Writer   
  1911. // GMFBridge Source (Output 2) -> (Audio Input 1) WM ASF Writer   
  1912. //   
  1913. HRESULT CDecklinkIngestDlg::CreateWMCaptureSinkGraph(IBaseFilter* pAVIMux, IBaseFilter* pFileWriter)   
  1914. {   
  1915.     HRESULT hr = S_OK;   
  1916.    
  1917.     if (pFileWriter)   
  1918.     {   
  1919.         hr = ConfigureWMEncoder(pFileWriter);   
  1920.         if (SUCCEEDED(hr))   
  1921.         {   
  1922.             if (false == m_bMuteAudio)   
  1923.             {   
  1924.                 // Connect the bridge audio output pin to the ASF writer.   
  1925.                 hr = CDSUtils::ConnectFilters(m_pSinkGraph, m_pBridgeSource, pFileWriter, &MEDIATYPE_Audio);   
  1926.             }   
  1927.                    
  1928.             if (SUCCEEDED(hr))   
  1929.             {   
  1930.                 // Connect the bridge video output pin to the ASF writer.   
  1931.                 hr = CDSUtils::ConnectFilters(m_pSinkGraph, m_pBridgeSource, pFileWriter, &MEDIATYPE_Video);   
  1932.             }   
  1933.         }   
  1934.     }   
  1935.     else   
  1936.     {   
  1937.         hr = E_POINTER;   
  1938.     }   
  1939.    
  1940.     return hr;   
  1941. }   
  1942.    
  1943. //-----------------------------------------------------------------------------   
  1944. // ConfigureWMEncoder   
  1945. // Configure the Windows Media encoder filter.   
  1946. HRESULT CDecklinkIngestDlg::ConfigureWMEncoder(IBaseFilter* pASFWriter)   
  1947. {   
  1948.     HRESULT hr = S_OK;   
  1949.    
  1950.     // Modify the video output resolution of a system profile.   
  1951.     if (pASFWriter)   
  1952.     {   
  1953.         // Simple system profile encoding.   
  1954.         CComQIPtr<iconfigasfwriter, &iid_iconfigasfwriter=""> pICW = pASFWriter;   
  1955.         if (pICW)   
  1956.         {   
  1957. //          NOTE: You could just use the following for a default system profile   
  1958. //          hr = pICW->ConfigureFilterUsingProfileGuid(WMProfile_XXX);   // RE: wmsysprf.h   
  1959.    
  1960. //          NOTE: If you want video only capture you must modify the profile to remove the audio   
  1961. //                otherwise encoding will fail   
  1962.    
  1963.             // Load a system profile and modify the resolution of the video output   
  1964.             // NOTE: The scope of the encoding is enormous, this just demonstrates how to change   
  1965.             // the output video resolution from 320x240 to something larger.   
  1966.             // Changing the resolution affects coding performance, it is likely that the encoder will   
  1967.             // start to drop frames after a while.  Using WM9 codecs will probably improve performance   
  1968.             // and that has been left to the reader... ;o)   
  1969.             //   
  1970.             // Get a profile manager.   
  1971.             CComPtr<iwmprofilemanager> pIWMProfileManager = NULL;   
  1972.             hr = WMCreateProfileManager(&pIWMProfileManager);   
  1973.             if (SUCCEEDED(hr))   
  1974.             {   
  1975.                 // Load a system profile to modify.   
  1976.                 CComPtr<iwmprofile> pIWMProfile = NULL;   
  1977.                 // NOTE: Any WMProfile_XXX could be used here, or create a custom profile from scratch.   
  1978.                 hr = pIWMProfileManager->LoadProfileByID(WMProfile_V80_FAIRVBRVideo, &pIWMProfile);   
  1979.                 if (SUCCEEDED(hr))   
  1980.                 {   
  1981.                     // Search the streams for the video stream and attempt to modify the video size.   
  1982.                     DWORD cbStreams = 0;   
  1983.                     hr = pIWMProfile->GetStreamCount(&cbStreams);   
  1984.                     if (SUCCEEDED(hr))   
  1985.                     {   
  1986.                         IWMStreamConfig* pIWMStreamConfig = NULL;   
  1987.                         GUID streamType = {0};   
  1988.                         DWORD stream;   
  1989.                            
  1990.                         if (m_bMuteAudio)   
  1991.                         {   
  1992.                             // Remove the audio stream for video only capture.   
  1993.                             for (stream=0; stream<cbstreams; ++stream)="" {="" hr="pIWMProfile-">GetStream(stream, &pIWMStreamConfig);   
  1994.                                 if (SUCCEEDED(hr))   
  1995.                                 {   
  1996.                                     hr = pIWMStreamConfig->GetStreamType(&streamType);   
  1997.                                     if (SUCCEEDED(hr))   
  1998.                                     {   
  1999.                                         if (MEDIATYPE_Audio == streamType)   
  2000.                                         {   
  2001.                                             if (SUCCEEDED(pIWMProfile->RemoveStream(pIWMStreamConfig)))   
  2002.                                             {   
  2003.                                                 --cbStreams;   
  2004.                                             }   
  2005.                                             SAFE_RELEASE(pIWMStreamConfig);   
  2006.                                             break;   
  2007.                                         }   
  2008.                                     }   
  2009.                                 }   
  2010.                             }   
  2011.                         }   
  2012.    
  2013.                         for (stream=0; stream<cbstreams; ++stream)="" {="" hr="pIWMProfile-">GetStream(stream, &pIWMStreamConfig);   
  2014.                             if (SUCCEEDED(hr))   
  2015.                             {   
  2016.                                 hr = pIWMStreamConfig->GetStreamType(&streamType);   
  2017.                                 if (SUCCEEDED(hr) && (MEDIATYPE_Video == streamType))   
  2018.                                 {   
  2019.                                     // Found the video stream.   
  2020.                                     CComQIPtr<iwmmediaprops, &iid_iwmmediaprops=""> pIWMMediaProps = pIWMStreamConfig;   
  2021.                                     if (pIWMMediaProps)   
  2022.                                     {   
  2023.                                         // Get the size of the media type.   
  2024.                                         WM_MEDIA_TYPE* pMediaType = NULL;   
  2025.                                         DWORD cbMediaType = 0;   
  2026.                                         hr = pIWMMediaProps->GetMediaType(pMediaType, &cbMediaType);   
  2027.                                         if (SUCCEEDED(hr))   
  2028.                                         {   
  2029.                                             pMediaType = (WM_MEDIA_TYPE*)new char [cbMediaType];   
  2030.                                             if (pMediaType)   
  2031.                                             {   
  2032.                                                 hr = pIWMMediaProps->GetMediaType(pMediaType, &cbMediaType);   
  2033.                                                 if (SUCCEEDED(hr))   
  2034.                                                 {   
  2035.                                                     BITMAPINFOHEADER* pbmih = NULL;   
  2036.                                                     if (WMFORMAT_VideoInfo == pMediaType->formattype)   
  2037.                                                     {   
  2038.                                                         WMVIDEOINFOHEADER* pvih = (WMVIDEOINFOHEADER*)pMediaType->pbFormat;   
  2039.                                                         pbmih = &pvih->bmiHeader;   
  2040.                                                     }   
  2041.                                                     else if (WMFORMAT_MPEG2Video == pMediaType->formattype)   
  2042.                                                     {   
  2043.                                                            
  2044.                                                         WMVIDEOINFOHEADER2* pvih = (WMVIDEOINFOHEADER2*)&((WMMPEG2VIDEOINFO*)pMediaType->pbFormat)->hdr;   
  2045.                                                         pbmih = &pvih->bmiHeader;   
  2046.                                                     }   
  2047.                                                        
  2048.                                                     if (pbmih)   
  2049.                                                     {   
  2050.                                                         // Modify the video dimensions, set the property, reconfigure the stream   
  2051.                                                         // and then configure the ASF writer with this modified profile.   
  2052.                                                         pbmih->biWidth = 640;    // was 320;   
  2053.                                                         pbmih->biHeight = 480;   // was 240;   
  2054.                                                         pbmih->biSizeImage = pbmih->biWidth * pbmih->biHeight * pbmih->biBitCount / 8;  // NOTE: This calculation is not correct for all bit depths.   
  2055.                                                         hr = pIWMMediaProps->SetMediaType(pMediaType);   
  2056.                                                         if (SUCCEEDED(hr))   
  2057.                                                         {   
  2058.                                                             // Config the ASF writer filter to use this modified system profile.   
  2059.                                                             hr = pIWMProfile->ReconfigStream(pIWMStreamConfig);   
  2060.                                                             if (SUCCEEDED(hr))   
  2061.                                                             {   
  2062.                                                                 hr = pICW->ConfigureFilterUsingProfile(pIWMProfile);   
  2063.                                                             }   
  2064.                                                         }   
  2065.                                                     }   
  2066.                                                 }   
  2067.                                                 delete [] (char*)pMediaType;   
  2068.                                             }   
  2069.                                         }   
  2070.                                     }   
  2071.                                 }   
  2072.                                 SAFE_RELEASE(pIWMStreamConfig);   
  2073.                             }   
  2074.                         }   
  2075.                     }   
  2076.                 }   
  2077.             }   
  2078. /*  
  2079.             // Modify other ASF writer properties.  
  2080.             IServiceProvider* pProvider = NULL;  
  2081.             hr = pASFWriter->QueryInterface(IID_IServiceProvider, reinterpret_cast<void**>(&pProvider));  
  2082.             if (SUCCEEDED(hr))  
  2083.             {  
  2084.                 IID_IWMWriterAdvanced2* pWMWA2 = NULL;  
  2085.                 hr = pProvider->QueryService(IID_IID_IWMWriterAdvanced2, IID_IID_IWMWriterAdvanced2, reinterpret_cast<void**>(&pWMWA2));  
  2086.                 if (SUCCEEDED(hr))  
  2087.                 {  
  2088.                     // Set the deinterlace mode.  
  2089.                     pWMWA2->GetInputSetting(...);  
  2090.                     :  
  2091.                     :  
  2092.                     SAFE_RELEASE(pWMWA2);  
  2093.                 }  
  2094.                 SAFE_RELEASE(pProvider);  
  2095.             }  
  2096. */   
  2097.         }   
  2098.     }   
  2099.     else   
  2100.     {   
  2101.         hr = E_INVALIDARG;   
  2102.     }   
  2103.    
  2104.     return hr;   
  2105. }   
  2106.    
  2107. //-----------------------------------------------------------------------------   
  2108. // CreateMJPEGCaptureSinkGraph   
  2109. // Create a graph for writing MJPEG compressed streams to disk using the AVI and   
  2110. // file writer filters.   
  2111. //   
  2112. // The following sink side graph is built:   
  2113. //   
  2114. // GMFBridge Source (Output 1) -> Decklink MJPEG Compressor -> (Input 01) AVI Mux -> File Writer   
  2115. // GMFBridge Source (Output 2) ------------------------------> (Input 02) AVI Mux   
  2116. //   
  2117. HRESULT CDecklinkIngestDlg::CreateMJPEGCaptureSinkGraph(IBaseFilter* pAVIMux, IBaseFilter* pFileWriter)   
  2118. {   
  2119.     HRESULT hr = S_OK;   
  2120.    
  2121.     if (pAVIMux && pFileWriter)   
  2122.     {   
  2123.         // Locate the DeckLink MJPEG encoder and add it to the graph.   
  2124.         CComPtr<ibasefilter> pEncoder = NULL;   
  2125.         hr = CDSUtils::AddFilter(m_pSinkGraph, CLSID_DecklinkMJPEGEncoderFilter, L"Decklink MJPEG Compressor", &pEncoder);   
  2126.         if (SUCCEEDED(hr))   
  2127.         {   
  2128.             // Configure the MJPEG encoder.   
  2129.             CComQIPtr<iamvideocompression, &iid_iamvideocompression=""> pIVC = pEncoder;   
  2130.             if (pIVC)   
  2131.             {   
  2132.                 long Capabilities;   
  2133.                 hr = pIVC->GetInfo(0, 0, 0, 0, 0, 0, 0, &Capabilities);   
  2134.                 if (SUCCEEDED(hr) && (Capabilities & CompressionCaps_CanQuality))   
  2135.                 {   
  2136.                     hr = pIVC->put_Quality(0.8);   
  2137.                 }   
  2138.             }   
  2139.    
  2140.             if (SUCCEEDED(hr))   
  2141.             {   
  2142.                 // Connect the bridge video output pin to the MJPEG Encoder.   
  2143.                 hr = CDSUtils::ConnectFilters(m_pSinkGraph, m_pBridgeSource, L"Output 1", pEncoder, NULL);   
  2144.                 if (SUCCEEDED(hr))   
  2145.                 {   
  2146.                     // Connect the MJPEG encoder output to the AVI mux.   
  2147.                     hr = CDSUtils::ConnectFilters(m_pSinkGraph, pEncoder, NULL, pAVIMux, NULL);   
  2148.                     if (SUCCEEDED(hr))   
  2149.                     {   
  2150.                         // Connect the mux to the file writer.   
  2151.                         hr = CDSUtils::ConnectFilters(m_pSinkGraph, pAVIMux, NULL, pFileWriter, NULL);   
  2152.                         if (SUCCEEDED(hr))   
  2153.                         {   
  2154.                             // Video path connected now optionally connect the audio path.   
  2155.                             if (false == m_bMuteAudio)   
  2156.                             {   
  2157.                                 // Connect the bridge audio output pin to the mux.   
  2158.                                 hr = CDSUtils::ConnectFilters(m_pSinkGraph, m_pBridgeSource, L"Output 2", pAVIMux, NULL);   
  2159.                             }   
  2160.                         }   
  2161.                     }   
  2162.                 }   
  2163.             }   
  2164.         }   
  2165.     }   
  2166.     else   
  2167.     {   
  2168.         hr = E_INVALIDARG;   
  2169.     }   
  2170.    
  2171.     return hr;   
  2172. }   
  2173.    
  2174. //-----------------------------------------------------------------------------   
  2175. // CreateStillSequenceCaptureSinkGraph   
  2176. // Create a graph for writing a sequence of stills to disk using the DeckLink   
  2177. // Still Sequence file writer filter.  The audio is written into a separate WAV   
  2178. // file using the DirectShow sample WAVDest filter.   
  2179. //   
  2180. // The following sink side graph is built:   
  2181. //   
  2182. // GMFBridge Source (Output 1) -> Decklink Still Sequence Sink   
  2183. // GMFBridge Source (Output 2) -> Wav Dest -> File Writer   
  2184. //   
  2185. HRESULT CDecklinkIngestDlg::CreateStillSequenceCaptureSinkGraph(IBaseFilter* pAVIMux, IBaseFilter* pFileWriter)   
  2186. {   
  2187.     HRESULT hr = S_OK;   
  2188.    
  2189.     if (pFileWriter)   
  2190.     {   
  2191.         // Create a directory using the base filename of the sequence of stills to capture the still sequence into.   
  2192.         LPOLESTR pszFilename;   
  2193.         hr = m_pIFileSinkFilter->GetCurFile(&pszFilename, NULL);   
  2194.         if (SUCCEEDED(hr))   
  2195.         {   
  2196.             basic_string<tchar> FilePath = COLE2T(pszFilename);   
  2197.             basic_string<tchar> Filename = FilePath;   
  2198.             CoTaskMemFree(pszFilename); // Release memory.   
  2199.            
  2200.             // Create the directory name by removing the file extension from the filename.   
  2201.             basic_string<tchar>::size_type Index = FilePath.rfind(_T('.'));   
  2202.             if (basic_string<tchar>::npos != Index)   
  2203.             {   
  2204.                 FilePath.erase(Index);   
  2205.             }   
  2206.    
  2207.             // Create the directory.   
  2208.             BOOL ret = CreateDirectory(FilePath.c_str(), NULL);   
  2209.             if (!ret && (ERROR_ALREADY_EXISTS != GetLastError()))   
  2210.             {   
  2211.                 hr = E_FAIL;   
  2212.             }   
  2213.    
  2214.             if (SUCCEEDED(hr))   
  2215.             {   
  2216.                 // Create the full filename including the newly created directory in the file path.   
  2217.                 Index = Filename.rfind(_T('//'));   
  2218.                 Filename = Filename.substr(Index);   
  2219.                 // Insert white space at the end of the filename so that the index of the capture is not used by the still sequence writer.   
  2220.                 Index = Filename.rfind(_T('.'));   
  2221.                 Filename.insert(Index, TEXT(" "));   
  2222.    
  2223.                 TCHAR szCaptureFilePath[MAX_PATH] = {0};   
  2224.                 StringCchPrintf(szCaptureFilePath, MAX_PATH, TEXT("%s"), FilePath.c_str());   
  2225.                 PathAppend(szCaptureFilePath, Filename.c_str());   
  2226.    
  2227.                 // Set the new filename.   
  2228.                 hr = m_pIFileSinkFilter->SetFileName(CT2OLE(szCaptureFilePath), NULL);   
  2229.                 if (SUCCEEDED(hr))   
  2230.                 {   
  2231.                     // Connect the bridge video output pin to the still sequence writer.   
  2232.                     hr = CDSUtils::ConnectFilters(m_pSinkGraph, m_pBridgeSource, L"Output 1", pFileWriter, NULL);   
  2233.                 }   
  2234.    
  2235.                 // Video path connected now optionally connect the audio path.   
  2236.                 if (SUCCEEDED(hr) && (false == m_bMuteAudio))   
  2237.                 {   
  2238.                     // Add the Microsoft DirectShow SDK WAV Dest sample filter.   
  2239.                     CComPtr<ibasefilter> pAudioDest;   
  2240.                     hr = CDSUtils::AddFilter(m_pSinkGraph, CLSID_WavDest, L"WAV Dest", &pAudioDest);   
  2241.                     if (SUCCEEDED(hr))   
  2242.                     {   
  2243.                         // Add a second file writer filter for the audio stream.   
  2244.                         CComPtr<ibasefilter> pFileWriter;   
  2245.                         hr = CDSUtils::AddFilter(m_pSinkGraph, CLSID_FileWriter, L"File Writer", &pFileWriter);   
  2246.                         if (SUCCEEDED(hr))   
  2247.                         {   
  2248.                             // Connect the bridge audio output pin to the WAV Dest filter.   
  2249.                             hr = CDSUtils::ConnectFilters(m_pSinkGraph, m_pBridgeSource, L"Output 2", pAudioDest, NULL);   
  2250.                             if (SUCCEEDED(hr))   
  2251.                             {   
  2252.                                 // Connect the WAV Dest filter to the file writer filter.   
  2253.                                 hr = CDSUtils::ConnectFilters(m_pSinkGraph, pAudioDest, NULL, pFileWriter, NULL);   
  2254.                                 if (SUCCEEDED(hr))   
  2255.                                 {   
  2256.                                     // Set the name of the audio file by using the base name of the sequence of stills.   
  2257.                                     CComQIPtr<ifilesinkfilter, &iid_ifilesinkfilter=""> pIFileSinkFilter = pFileWriter;   
  2258.                                     if (pIFileSinkFilter)   
  2259.                                     {   
  2260.                                         Filename = szCaptureFilePath;   
  2261.                                         Index = Filename.rfind(_T('.'));   
  2262.                                         if (basic_string<tchar>::npos != Index)   
  2263.                                         {   
  2264.                                             Filename.erase(Index);   
  2265.                                         }   
  2266.                                         Filename.append(TEXT(".wav"));   
  2267.    
  2268.                                         hr = pIFileSinkFilter->SetFileName(CT2OLE(Filename.c_str()), NULL);   
  2269.                                     }   
  2270.                                 }   
  2271.                             }   
  2272.                         }   
  2273.                     }   
  2274.                 }   
  2275.             }   
  2276.         }   
  2277.     }   
  2278.     else   
  2279.     {   
  2280.         hr = E_INVALIDARG;   
  2281.     }   
  2282.    
  2283.     return hr;   
  2284. }   
  2285.    
  2286. //-----------------------------------------------------------------------------   
  2287. // DestroySourceGraph   
  2288. // Remove all filters from the source side graph being careful to release and   
  2289. // associated resources.   
  2290. HRESULT CDecklinkIngestDlg::DestroySourceGraph(void)   
  2291. {   
  2292.     HRESULT hr = S_OK;   
  2293.    
  2294.     CComQIPtr<imediacontrol, &iid_imediacontrol=""> pIMediaControl = m_pSourceGraph;   
  2295.     if (pIMediaControl)   
  2296.     {   
  2297.         // Stop playback.   
  2298.         pIMediaControl->Stop();   
  2299.     }   
  2300.    
  2301.     // As the sources are to be removed, release the outstanding references on them.   
  2302.     m_pIGMFBridgeController = NULL;   
  2303.     m_pIExtTransport = (IAMExtTransport*)NULL;   
  2304.     m_pVideoDevice = m_pAudioDevice = NULL;   
  2305.        
  2306.     if (m_pSourceGraph)   
  2307.     {   
  2308.         CComPtr<ienumfilters> pEnum = NULL;   
  2309.         hr = m_pSourceGraph->EnumFilters(&pEnum);   
  2310.         if (SUCCEEDED(hr))   
  2311.         {   
  2312.             IBaseFilter* pFilter = NULL;   
  2313.             while (S_OK == pEnum->Next(1, &pFilter, NULL))   
  2314.             {   
  2315.                 CDSUtils::DisconnectPins(m_pSourceGraph, pFilter);   
  2316.    
  2317.                 FILTER_INFO filterInfo = {0};   
  2318.                 hr = pFilter->QueryFilterInfo(&filterInfo);   
  2319.                 if (SUCCEEDED(hr))   
  2320.                 {   
  2321.                     SAFE_RELEASE(filterInfo.pGraph);   
  2322.                        
  2323.                     if ((NULL == wcsstr(filterInfo.achName, SMARTT_NAME)) && (NULL == wcsstr(filterInfo.achName, VIDEORENDERER_NAME)) &&   
  2324.                         (NULL == wcsstr(filterInfo.achName, SAMPLEGRABBERSTILLS_NAME)) && (NULL == wcsstr(filterInfo.achName, SAMPLEGRABBERTIMECODE_NAME)) &&   
  2325.                         (NULL == wcsstr(filterInfo.achName, NULLRENDERER_NAME)))   
  2326.                     {   
  2327.                         hr = m_pSourceGraph->RemoveFilter(pFilter);   
  2328.                         if (SUCCEEDED(hr))   
  2329.                         {   
  2330.                             hr = pEnum->Reset();   
  2331.                         }   
  2332.                     }   
  2333.                 }   
  2334.                 SAFE_RELEASE(pFilter);   
  2335.             }   
  2336.         }   
  2337.     }   
  2338.     else   
  2339.     {   
  2340.         hr = E_POINTER;   
  2341.     }   
  2342.    
  2343.     return hr;   
  2344. }   
  2345.    
  2346. //-----------------------------------------------------------------------------   
  2347. // DestroySinkGraph   
  2348. // Remove all filters from the sink side graph.   
  2349. HRESULT CDecklinkIngestDlg::DestroySinkGraph(void)   
  2350. {   
  2351.     HRESULT hr = S_OK;   
  2352.    
  2353.     CComQIPtr<imediacontrol, &iid_imediacontrol=""> pIMediaControl = m_pSinkGraph;   
  2354.     if (pIMediaControl)   
  2355.     {   
  2356.         // Stop playback.   
  2357.         pIMediaControl->Stop();   
  2358.     }   
  2359.        
  2360.     if (m_pSinkGraph)   
  2361.     {   
  2362.         CComPtr<ienumfilters> pEnum = NULL;   
  2363.         hr = m_pSinkGraph->EnumFilters(&pEnum);   
  2364.         if (SUCCEEDED(hr))   
  2365.         {   
  2366.             IBaseFilter* pFilter = NULL;   
  2367.             while (S_OK == pEnum->Next(1, &pFilter, NULL))   
  2368.             {   
  2369.                 CDSUtils::DisconnectPins(m_pSinkGraph, pFilter);   
  2370.    
  2371.                 FILTER_INFO filterInfo = {0};   
  2372.                 hr = pFilter->QueryFilterInfo(&filterInfo);   
  2373.                 if (SUCCEEDED(hr))   
  2374.                 {   
  2375.                     SAFE_RELEASE(filterInfo.pGraph);   
  2376.                        
  2377.                     hr = m_pSinkGraph->RemoveFilter(pFilter);   
  2378.                     if (SUCCEEDED(hr))   
  2379.                     {   
  2380.                         hr = pEnum->Reset();   
  2381.                     }   
  2382.                 }   
  2383.                 SAFE_RELEASE(pFilter);   
  2384.             }   
  2385.         }   
  2386.     }   
  2387.     else   
  2388.     {   
  2389.         hr = E_POINTER;   
  2390.     }   
  2391.    
  2392.     return hr;   
  2393. }   
  2394.    
  2395. //-----------------------------------------------------------------------------   
  2396. // StartCapture   
  2397. //   
  2398. HRESULT CDecklinkIngestDlg::StartCapture(void)   
  2399. {   
  2400.     HRESULT hr = S_OK;   
  2401.     TRACE(TEXT("CDecklinkIngestDlg::StartCapture()/r/n"));   
  2402.    
  2403.     CComQIPtr<imediacontrol, &iid_imediacontrol=""> pIMediaControl = m_pSinkGraph;   
  2404.     if (pIMediaControl)   
  2405.     {   
  2406.         // Run the capture sink graph.   
  2407.         hr = pIMediaControl->Run();   
  2408.         TRACE(TEXT("CDecklinkIngestDlg::StartCapture() - Running sink graph./r/n"));   
  2409.    
  2410.         // Close the bridge.   
  2411.         if (m_pIGMFBridgeController)   
  2412.         {   
  2413.             hr = m_pIGMFBridgeController->BridgeGraphs(m_pBridgeSink, m_pBridgeSource);   
  2414.             TRACE(TEXT("CDecklinkIngestDlg::StartCapture() - Closing the bridge./r/n"));   
  2415.         }   
  2416.     }   
  2417.    
  2418.     return hr;   
  2419. }   
  2420.    
  2421. //-----------------------------------------------------------------------------   
  2422. // StopCapture   
  2423. //   
  2424. HRESULT CDecklinkIngestDlg::StopCapture(void)   
  2425. {   
  2426.     HRESULT hr = S_OK;   
  2427.     TRACE(TEXT("CDecklinkIngestDlg::StopCapture()/r/n"));   
  2428.     // Open the bridge.   
  2429.     if (m_pIGMFBridgeController)   
  2430.     {   
  2431.         TRACE(TEXT("CDecklinkIngestDlg::StopCapture() - Opening the bridge./r/n"));   
  2432.         hr = m_pIGMFBridgeController->BridgeGraphs(NULL, NULL);   
  2433.     }   
  2434.    
  2435.     // Stop the capture sink graph so that the file writing can be finalised.   
  2436.     CComQIPtr<imediacontrol, &iid_imediacontrol=""> pIMediaControl = m_pSinkGraph;   
  2437.     if (pIMediaControl)   
  2438.     {   
  2439.         TRACE(TEXT("CDecklinkIngestDlg::StopCapture() - Stopping sink graph./r/n"));   
  2440.         hr = pIMediaControl->Stop();   
  2441.     }   
  2442.    
  2443.     // Stop the capture streams.   
  2444.     hr = ConfigureSourceGraph(NULL, NULL);   
  2445.    
  2446.     return hr;   
  2447. }   
  2448.    
  2449. //-----------------------------------------------------------------------------   
  2450. // EnableSampleGrabberCallback   
  2451. // Enable or disable the sample grabber used for capturing image stills.   
  2452. HRESULT CDecklinkIngestDlg::EnableSampleGrabberCallback(bool bEnable)   
  2453. {   
  2454.     HRESULT hr = S_OK;   
  2455.        
  2456.     if (m_pISampleGrabberStills)   
  2457.     {   
  2458.         m_pISampleGrabberStills->SetCallback(bEnable ? m_pStillGrabber : NULL, 0);   
  2459.     }   
  2460.     else   
  2461.     {   
  2462.         hr = E_POINTER;   
  2463.     }   
  2464.        
  2465.     return hr;   
  2466. }   
  2467.    
  2468. //-----------------------------------------------------------------------------   
  2469. // EnableWindows   
  2470. // Sets the state of a group of controls.   
  2471. void CDecklinkIngestDlg::EnableWindows(const int* pCtrlIDs, int cCtrlIDs, BOOL bEnable)   
  2472. {   
  2473.     if (pCtrlIDs)   
  2474.     {   
  2475.         for (int Ctrl=0; Ctrl<cctrlids; ++ctrl)="" {="" cwnd*="" pwnd="GetDlgItem(pCtrlIDs[Ctrl]);" if="" (pwnd)="" pwnd-="">EnableWindow(bEnable);   
  2476.             }   
  2477.         }   
  2478.     }   
  2479. }   
  2480.    
  2481. //-----------------------------------------------------------------------------   
  2482. // QueryRegistry   
  2483. // retrieve previous media formats from registry   
  2484. void CDecklinkIngestDlg::QueryRegistry(void)   
  2485. {   
  2486.     if (ERROR_SUCCESS == m_RegUtils.Open(TEXT("DecklinkIngestSample")))   
  2487.     {   
  2488.         TCHAR szBuffer[MAX_PATH];   
  2489.         ZeroMemory(szBuffer, sizeof(szBuffer));   
  2490.         EXECUTE_ASSERT(ERROR_SUCCESS == m_RegUtils.GetString(TEXT("CaptureFilePath"), reinterpret_cast<lpbyte>(szBuffer), sizeof(szBuffer)));   
  2491.         m_CaptureFilePath = szBuffer;   
  2492.    
  2493.         ZeroMemory(szBuffer, sizeof(szBuffer));   
  2494.         EXECUTE_ASSERT(ERROR_SUCCESS == m_RegUtils.GetString(TEXT("CaptureFilename"), reinterpret_cast<lpbyte>(szBuffer), sizeof(szBuffer)));   
  2495.         m_CaptureFilename = szBuffer;   
  2496.    
  2497.         AM_MEDIA_TYPE amt;   
  2498.         if (ERROR_SUCCESS == m_RegUtils.GetBinary(TEXT("VideoMediaType"), reinterpret_cast<lpbyte>(&amt), sizeof(amt)))   
  2499.         {   
  2500.             VIDEOINFOHEADER vih = {0};   
  2501.             if (ERROR_SUCCESS == m_RegUtils.GetBinary(TEXT("VideoFormat"), reinterpret_cast<lpbyte>(&vih), sizeof(vih)))   
  2502.             {   
  2503.                 amt.cbFormat = sizeof(vih);   
  2504.                 amt.pbFormat = reinterpret_cast<byte*>(&vih);   
  2505.                 m_VideoFormat.Set(amt);   
  2506.             }   
  2507.         }   
  2508.    
  2509.         if (ERROR_SUCCESS == m_RegUtils.GetBinary(TEXT("AudioMediaType"), reinterpret_cast<lpbyte>(&amt), sizeof(amt)))   
  2510.         {   
  2511.             WAVEFORMATEX wfex = {0};   
  2512.             if (ERROR_SUCCESS == m_RegUtils.GetBinary(TEXT("AudioFormat"), reinterpret_cast<lpbyte>(&wfex), sizeof(wfex)))   
  2513.             {   
  2514.                 amt.cbFormat = sizeof(wfex);   
  2515.                 amt.pbFormat = reinterpret_cast<byte*>(&wfex);   
  2516.                 m_AudioFormat.Set(amt);   
  2517.             }   
  2518.         }   
  2519.    
  2520.         EXECUTE_ASSERT(ERROR_SUCCESS == m_RegUtils.GetBinary(TEXT("AudioMute"), reinterpret_cast<lpbyte>(&m_bMuteAudio), sizeof(m_bMuteAudio)));   
  2521.         EXECUTE_ASSERT(ERROR_SUCCESS == m_RegUtils.GetBinary(TEXT("VideoCompressor"), reinterpret_cast<lpbyte>(&m_Compression), sizeof(m_Compression)));   
  2522.            
  2523.         EXECUTE_ASSERT(ERROR_SUCCESS == m_RegUtils.GetBinary(TEXT("VideoCaptureDevice"), reinterpret_cast<lpbyte>(&m_Device), sizeof(m_Device)));   
  2524.         if (CDecklinkIngestDlg::MAXDEVICES <= m_Device)   
  2525.         {   
  2526.             m_Device = 0;   
  2527.         }   
  2528.     }   
  2529.     else   
  2530.     {   
  2531.         // create the key and registry values   
  2532.         if (ERROR_SUCCESS == m_RegUtils.Create(TEXT("DecklinkIngestSample")))   
  2533.         {   
  2534.             SavePreferencesToRegistry();   
  2535.             EXECUTE_ASSERT(ERROR_SUCCESS == m_RegUtils.SetBinary(TEXT("VideoCaptureDevice"), reinterpret_cast<lpbyte>(&m_Device), sizeof(m_Device)));   
  2536.         }   
  2537.     }   
  2538. }   
  2539.    
  2540. //-----------------------------------------------------------------------------   
  2541. // SavePreferencesToRegistry   
  2542. // retrieve previous media formats from registry   
  2543. void CDecklinkIngestDlg::SavePreferencesToRegistry(void)   
  2544. {   
  2545.     EXECUTE_ASSERT(ERROR_SUCCESS == m_RegUtils.SetString(TEXT("CaptureFilePath"), reinterpret_cast<const byte*="">(m_CaptureFilePath.c_str()), (DWORD)m_CaptureFilePath.length()));   
  2546.     EXECUTE_ASSERT(ERROR_SUCCESS == m_RegUtils.SetString(TEXT("CaptureFilename"), reinterpret_cast<const byte*="">(m_CaptureFilename.c_str()), (DWORD)m_CaptureFilename.length()));   
  2547.    
  2548.     AM_MEDIA_TYPE amt = m_VideoFormat;   
  2549.     amt.cbFormat = 0;   
  2550.     amt.pbFormat = NULL;   
  2551.     if (ERROR_SUCCESS == m_RegUtils.SetBinary(TEXT("VideoMediaType"), reinterpret_cast<lpbyte>(&amt), sizeof(amt)))   
  2552.     {   
  2553.         EXECUTE_ASSERT(ERROR_SUCCESS == m_RegUtils.SetBinary(TEXT("VideoFormat"), m_VideoFormat.Format(), sizeof(VIDEOINFOHEADER)));   
  2554.     }   
  2555.    
  2556.     amt = m_AudioFormat;   
  2557.     amt.cbFormat = 0;   
  2558.     amt.pbFormat = NULL;   
  2559.     if (ERROR_SUCCESS == m_RegUtils.SetBinary(TEXT("AudioMediaType"), reinterpret_cast<lpbyte>(&amt), sizeof(amt)))   
  2560.     {   
  2561.         EXECUTE_ASSERT(ERROR_SUCCESS == m_RegUtils.SetBinary(TEXT("AudioFormat"), m_AudioFormat.Format(), m_AudioFormat.FormatLength()));   
  2562.     }   
  2563.    
  2564.     EXECUTE_ASSERT(ERROR_SUCCESS == m_RegUtils.SetBinary(TEXT("AudioMute"), reinterpret_cast<lpbyte>(&m_bMuteAudio), sizeof(m_bMuteAudio)));   
  2565.     EXECUTE_ASSERT(ERROR_SUCCESS == m_RegUtils.SetBinary(TEXT("VideoCompressor"), reinterpret_cast<lpbyte>(&m_Compression), sizeof(m_Compression)));   
  2566. }   
  2567.    
  2568. //-----------------------------------------------------------------------------   
  2569. // SetFileExtension   
  2570. //   
  2571. void CDecklinkIngestDlg::SetFileExtension(basic_string<tchar>& Filename)   
  2572. {   
  2573.     basic_string<tchar>::size_type Index = Filename.rfind(_T('.'));   
  2574.     if (basic_string<tchar>::npos != Index)   
  2575.     {   
  2576.         Filename.erase(Index);   
  2577.     }   
  2578.    
  2579.     if (ENC_WM == m_Compression)   
  2580.     {   
  2581.         m_CaptureFileExtension = TEXT(".wmv");   
  2582.     }   
  2583.     else if (ENC_SEQ_CIN == m_Compression)   
  2584.     {   
  2585.         m_CaptureFileExtension = TEXT(".cin");   
  2586.     }   
  2587.     else if (ENC_SEQ_DPX == m_Compression)   
  2588.     {   
  2589.         m_CaptureFileExtension = TEXT(".dpx");   
  2590.     }   
  2591.     else if (ENC_SEQ_TGA == m_Compression)   
  2592.     {   
  2593.         m_CaptureFileExtension = TEXT(".tga");   
  2594.     }   
  2595.     else if (ENC_SEQ_BMP == m_Compression)   
  2596.     {   
  2597.         m_CaptureFileExtension = TEXT(".bmp");   
  2598.     }   
  2599.     else   
  2600.     {   
  2601.         m_CaptureFileExtension = TEXT(".avi");   
  2602.     }   
  2603. }   
  2604.    
  2605. //-----------------------------------------------------------------------------   
  2606. // CreateStatusBarControl   
  2607. //   
  2608. void CDecklinkIngestDlg::CreateStatusBarControl(void)   
  2609. {   
  2610.     // Create and configure the status bar control.   
  2611.     CRect Rect;   
  2612.     GetWindowRect(&Rect);   
  2613.     SetWindowPos(NULL, Rect.left, Rect.top, Rect.right - Rect.left, (Rect.bottom - Rect.top) + 25, 0);   
  2614.     VERIFY(m_StatusBarCtrl.Create(WS_CHILD | WS_VISIBLE | CCS_BOTTOM, CRect(0, 0, 0, 0), this, IDC_STATUS_BAR));   
  2615.     m_StatusBarCtrl.GetClientRect(&Rect);   
  2616.     int Widths[] = { 350, -1 };   
  2617.     VERIFY(m_StatusBarCtrl.SetParts(SIZEOF_ARRAY(Widths), Widths));   
  2618.    
  2619.     // The idea is to have the progress control sandwiched between two timecodes,   
  2620.     // one indicating the progess so far and the other the progress remaining.   
  2621.    
  2622.     // First, determine the size of the text.   
  2623.     CClientDC dc(&m_StatusBarCtrl);   
  2624.     CFont* pOldFont = dc.SelectObject(m_StatusBarCtrl.GetFont());   
  2625.     CSize LeftSide = dc.GetTextExtent(CString(TEXT("Capturing...00:00:00:00")));   
  2626.     CSize Space = dc.GetTextExtent(CString(TEXT(" ")));   
  2627.     dc.SelectObject(pOldFont);   
  2628.    
  2629.     // Second, adjust the size of the progress control to fit between the timecode   
  2630.     // strings indicating the current batch capture progress.   
  2631.     m_StatusBarCtrl.GetRect(0, &Rect);   
  2632.     Rect.DeflateRect(3, 3);   
  2633.     Rect.left += LeftSide.cx;   
  2634.     Rect.right = Rect.left + (51 * Space.cx);   
  2635.    
  2636.     // Create and configure the status bar progess control.   
  2637.     VERIFY(m_ProgressCtrl.Create(WS_CHILD, Rect, &m_StatusBarCtrl, IDC_STATUS_BAR_PROGRESS));   
  2638.     m_ProgressCtrl.SetRange(0, 100);   
  2639.     m_ProgressCtrl.SetStep(1); 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值