- //-----------------------------------------------------------------------------
- // $Id: DecklinkIngestDlg.cpp,v 1.1 2007/05/07 01:09:29 ivanr Exp $
- //
- // Desc: DirectShow ingest sample
- //
- // Copyright (c) Blackmagic Design 2007. All rights reserved.
- //-----------------------------------------------------------------------------
- #include "stdafx.h"
- #include "DecklinkIngest.h"
- #include "DecklinkIngestDlg.h"
- #include "PreferencesDlg.h"
- #include "DeviceInfoDlg.h"
- #include "Timecode.h"
- #include "StaticURL.h"
- #include <xprtdefs.h> // device control
- #include "CMX3600EDLReader.h"
- #include "DecklinkFilters_h.h"
- #ifdef _DEBUG
- #define new DEBUG_NEW
- #endif
- //-----------------------------------------------------------------------------
- // CAboutDlg dialog used for App About
- //-----------------------------------------------------------------------------
- class CAboutDlg : public CDialog
- {
- public:
- CAboutDlg();
- // Dialog Data
- enum { IDD = IDD_ABOUTBOX };
- protected:
- virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
- // Implementation
- protected:
- CStaticURL m_LinkBMD;
- CStaticURL m_LinkGDCL;
- virtual BOOL OnInitDialog();
- DECLARE_MESSAGE_MAP()
- };
- //-----------------------------------------------------------------------------
- // Constructor
- //
- CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
- {
- }
- //-----------------------------------------------------------------------------
- // DoDataExchange
- //
- void CAboutDlg::DoDataExchange(CDataExchange* pDX)
- {
- CDialog::DoDataExchange(pDX);
- }
- //-----------------------------------------------------------------------------
- // Message map
- //
- BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
- END_MESSAGE_MAP()
- //-----------------------------------------------------------------------------
- // OnInitDialog
- // Subclass the static controls that display URLs.
- BOOL CAboutDlg::OnInitDialog()
- {
- CDialog::OnInitDialog();
- m_LinkBMD.SubclassDlgItem(IDC_STATIC_URLBMD, this);
- m_LinkGDCL.SubclassDlgItem(IDC_STATIC_URLGDCL, this);
- return FALSE;
- }
- //-----------------------------------------------------------------------------
- // CDecklinkIngestDlg dialog
- //-----------------------------------------------------------------------------
- const int CDecklinkIngestDlg::MAXDEVICES = 256;
- static LPCWSTR SMARTT_NAME = L"Smart Tee";
- static LPCWSTR VIDEORENDERER_NAME = L"Video Renderer";
- static LPCWSTR SAMPLEGRABBERSTILLS_NAME = L"Sample Grabber Stills";
- static LPCWSTR SAMPLEGRABBERTIMECODE_NAME = L"Sample Grabber Timecode";
- static LPCWSTR NULLRENDERER_NAME = L"Null Renderer";
- static const int LogClipCtrlIDs[] = {IDC_BUTTON_REMOVECLIP, IDC_BUTTON_MOVECLIPUP, IDC_BUTTON_MOVECLIPDOWN};
- static const int CaptureCtrlIDs[] = {IDC_BUTTON_CAPTURENOW, IDC_BUTTON_BATCHCAPTURE, IDC_BUTTON_CAPTURESTILL};
- static const int TransportCtrlIDs[] = {IDC_BUTTON_FFWD, IDC_BUTTON_FREW, IDC_BUTTON_PLAY, IDC_BUTTON_STOP, IDC_BUTTON_EJECT, IDC_BUTTON_BATCHCAPTURE };
- //-----------------------------------------------------------------------------
- // Constructor
- //
- CDecklinkIngestDlg::CDecklinkIngestDlg(CWnd* pParent /*=NULL*/)
- : CDialog(CDecklinkIngestDlg::IDD, pParent)
- {
- m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
- }
- //-----------------------------------------------------------------------------
- // DoDataExchange
- //
- void CDecklinkIngestDlg::DoDataExchange(CDataExchange* pDX)
- {
- CDialog::DoDataExchange(pDX);
- DDX_Control(pDX, IDC_BUTTON_LOGCLIP, m_BtnAddClip);
- DDX_Control(pDX, IDC_BUTTON_REMOVECLIP, m_BtnRemoveClip);
- DDX_Control(pDX, IDC_BUTTON_MOVECLIPUP, m_BtnMoveClipUp);
- DDX_Control(pDX, IDC_BUTTON_MOVECLIPDOWN, m_BtnMoveClipDown);
- DDX_Control(pDX, IDC_LIST_CLIPS, m_ClipList);
- DDX_Control(pDX, IDC_STATIC_TIMECODE, m_TimecodeCtrl);
- DDX_Control(pDX, IDC_EDIT_INPOINT, m_InpointCtrl);
- DDX_Control(pDX, IDC_EDIT_OUTPOINT, m_OutpointCtrl);
- DDX_Control(pDX, IDC_STATIC_DURATION, m_DurationCtrl);
- DDX_Control(pDX, IDC_BUTTON_FREW, m_BtnFRew);
- DDX_Control(pDX, IDC_BUTTON_PLAY, m_BtnPlay);
- DDX_Control(pDX, IDC_BUTTON_FFWD, m_BtnFFwd);
- DDX_Control(pDX, IDC_BUTTON_STOP, m_BtnStop);
- DDX_Control(pDX, IDC_BUTTON_EJECT, m_BtnEjct);
- DDX_Control(pDX, IDC_EDIT_TAPENAME, m_TapenameCtrl);
- DDX_Control(pDX, IDC_EDIT_CLIPNAME, m_ClipnameCtrl);
- }
- //-----------------------------------------------------------------------------
- // Message map
- //
- BEGIN_MESSAGE_MAP(CDecklinkIngestDlg, CDialog)
- ON_WM_SYSCOMMAND()
- ON_WM_PAINT()
- ON_WM_QUERYDRAGICON()
- ON_COMMAND(ID_FILE_IMPORTEDL, OnFileImportEDL)
- ON_COMMAND(ID_EDIT_PREFERENCES, OnEditPreferences)
- ON_COMMAND_RANGE(IDC_DEVICE_BASE, IDC_DEVICE_BASE + CDecklinkIngestDlg::MAXDEVICES - 1, OnDeviceMenu)
- ON_COMMAND(IDC_DEVICE_INFO, OnDeviceInfo)
- ON_COMMAND(IDM_ABOUTBOX, OnAboutBox)
- ON_WM_INITMENUPOPUP()
- //}}AFX_MSG_MAP
- ON_BN_CLICKED(IDC_BUTTON_LOGCLIP, OnBnClickedButtonLogclip)
- ON_BN_CLICKED(IDC_BUTTON_REMOVECLIP, OnBnClickedButtonRemoveclip)
- ON_BN_CLICKED(IDC_BUTTON_MOVECLIPUP, OnBnClickedButtonMoveclipup)
- ON_BN_CLICKED(IDC_BUTTON_MOVECLIPDOWN, OnBnClickedButtonMoveclipdown)
- ON_BN_CLICKED(IDC_BUTTON_CAPTURESTILL, OnBnClickedButtonCaptureStill)
- ON_BN_CLICKED(IDC_BUTTON_CAPTURENOW, OnBnClickedButtonCaptureNow)
- ON_BN_CLICKED(IDC_BUTTON_BATCHCAPTURE, OnBnClickedButtonBatchCapture)
- ON_BN_CLICKED(IDC_BUTTON_ABORT, OnBnClickedButtonAbort)
- ON_BN_CLICKED(IDC_BUTTON_PLAY, OnBnClickedButtonPlay)
- ON_BN_CLICKED(IDC_BUTTON_STOP, OnBnClickedButtonStop)
- ON_BN_CLICKED(IDC_BUTTON_FREW, OnBnClickedButtonFrew)
- ON_BN_CLICKED(IDC_BUTTON_FFWD, OnBnClickedButtonFfwd)
- ON_BN_CLICKED(IDC_BUTTON_EJECT, OnBnClickedButtonEject)
- END_MESSAGE_MAP()
- //-----------------------------------------------------------------------------
- // OnInitDialog
- //
- BOOL CDecklinkIngestDlg::OnInitDialog()
- {
- CDialog::OnInitDialog();
- // Add "About..." menu item to system menu.
- // IDM_ABOUTBOX must be in the system command range.
- ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
- ASSERT(IDM_ABOUTBOX < 0xF000);
- CMenu* pSysMenu = GetSystemMenu(FALSE);
- if (pSysMenu != NULL)
- {
- CString strAboutMenu;
- strAboutMenu.LoadString(IDS_ABOUTBOX);
- if (!strAboutMenu.IsEmpty())
- {
- pSysMenu->AppendMenu(MF_SEPARATOR);
- pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
- }
- }
- // Set the icon for this dialog. The framework does this automatically
- // when the application's main window is not a dialog
- SetIcon(m_hIcon, TRUE); // Set big icon
- SetIcon(m_hIcon, FALSE); // Set small icon
- // TODO: Add extra initialization here
- // Create the sample grabber callback handlers.
- m_pStillGrabber = new CStillGrabber;
- m_pTimecodeGrabber = new CTimecodeGrabber;
- m_pDeviceNameVideo = NULL;
- CreateStatusBarControl();
- m_hThread = NULL;
- m_hExitEvent = NULL;
- // Set the defaults capture location and filename.
- TCHAR szPath[MAX_PATH] = {0};
- if (S_OK == SHGetFolderPath(NULL, CSIDL_PERSONAL, NULL, SHGFP_TYPE_CURRENT, szPath))
- {
- if (INVALID_FILE_ATTRIBUTES != GetFileAttributes(szPath))
- {
- PathAppend(szPath, TEXT("My Videos"));
- m_CaptureFilePath = szPath;
- }
- }
- m_CaptureFilename = TEXT("DecklinkIngest");
- m_CaptureTapename = TEXT("Untitled Tape");
- // Set the default video capture format.
- m_VideoFormat.InitMediaType();
- m_VideoFormat.SetType(&MEDIATYPE_Video);
- m_VideoFormat.SetSubtype(&MEDIASUBTYPE_UYVY);
- m_VideoFormat.SetFormatType(&FORMAT_VideoInfo);
- VIDEOINFOHEADER* pvih = (VIDEOINFOHEADER*)m_VideoFormat.AllocFormatBuffer(sizeof(VIDEOINFOHEADER));
- if (pvih)
- {
- ZeroMemory(pvih, sizeof(VIDEOINFOHEADER));
- pvih->AvgTimePerFrame = 333667;
- pvih->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
- pvih->bmiHeader.biWidth = 720;
- pvih->bmiHeader.biHeight = 486;
- pvih->bmiHeader.biPlanes = 1;
- pvih->bmiHeader.biBitCount = 16;
- pvih->bmiHeader.biCompression = 'YVYU';
- pvih->bmiHeader.biSizeImage = pvih->bmiHeader.biWidth * pvih->bmiHeader.biHeight * pvih->bmiHeader.biBitCount / 8;
- m_VideoFormat.SetSampleSize(pvih->bmiHeader.biSizeImage);
- pvih->dwBitRate = pvih->bmiHeader.biSizeImage * 8 * 30;
- }
- // Set the default audio capture format.
- m_AudioFormat.InitMediaType();
- m_AudioFormat.SetType(&MEDIATYPE_Audio);
- m_AudioFormat.SetSubtype(&MEDIASUBTYPE_PCM);
- m_AudioFormat.SetFormatType(&FORMAT_WaveFormatEx);
- WAVEFORMATEX* pwfex = (WAVEFORMATEX*)m_AudioFormat.AllocFormatBuffer(sizeof(WAVEFORMATEX));
- if (pwfex)
- {
- ZeroMemory(pwfex, sizeof(WAVEFORMATEX));
- pwfex->wFormatTag = WAVE_FORMAT_PCM;
- pwfex->nChannels = 2;
- pwfex->nSamplesPerSec = 48000;
- pwfex->wBitsPerSample = 16;
- pwfex->nBlockAlign = pwfex->wBitsPerSample * pwfex->nChannels / 8;
- pwfex->nAvgBytesPerSec = pwfex->nSamplesPerSec * pwfex->nBlockAlign;
- }
- m_bMuteAudio = false;
- m_Compression = ENC_NONE;
- m_Device = 0;
- ResetFileIndex();
- // load bitmaps for file buttons
- m_BtnAddClip.LoadBitmaps(IDB_BITMAP_ADDFILEU, IDB_BITMAP_ADDFILED, IDB_BITMAP_ADDFILEF, IDB_BITMAP_ADDFILEX);
- m_BtnRemoveClip.LoadBitmaps(IDB_BITMAP_REMOVEFILEU, IDB_BITMAP_REMOVEFILED, IDB_BITMAP_REMOVEFILEF, IDB_BITMAP_REMOVEFILEX);
- m_BtnMoveClipUp.LoadBitmaps(IDB_BITMAP_MOVEUPFILEU, IDB_BITMAP_MOVEUPFILED, IDB_BITMAP_MOVEUPFILEF, IDB_BITMAP_MOVEUPFILEX);
- m_BtnMoveClipDown.LoadBitmaps(IDB_BITMAP_MOVEDOWNFILEU, IDB_BITMAP_MOVEDOWNFILED, IDB_BITMAP_MOVEDOWNFILEF, IDB_BITMAP_MOVEDOWNFILEX);
- // set the columns in the list control
- int ColumnWidth = m_ClipList.GetStringWidth(TEXT("00:00:00:00")) + 12;
- m_ClipList.InsertColumn(0, TEXT("Clip name"), LVCFMT_CENTER, 150);
- m_ClipList.InsertColumn(1, TEXT("In-point"), LVCFMT_CENTER, ColumnWidth);
- m_ClipList.InsertColumn(2, TEXT("Out-point"), LVCFMT_CENTER, ColumnWidth);
- m_ClipList.InsertColumn(3, TEXT("Duration"), LVCFMT_CENTER, ColumnWidth);
- m_ClipList.InsertColumn(4, TEXT("Tape name"), LVCFMT_CENTER, 150);
- m_InpointCtrl.SetWindowText(TEXT("00:00:00:00"));
- m_OutpointCtrl.SetWindowText(TEXT("00:00:00:00"));
- m_TapenameCtrl.SetWindowText(TEXT("Untitled Tape"));
- m_ClipnameCtrl.SetWindowText(TEXT("Untitled Clip"));
- // load bitmaps for the deck controls
- m_BtnFRew.LoadBitmaps(IDB_BITMAP_FREWU, IDB_BITMAP_FREWD, IDB_BITMAP_FREWF, IDB_BITMAP_FREWX);
- m_BtnPlay.LoadBitmaps(IDB_BITMAP_PLAYU, IDB_BITMAP_PLAYD, IDB_BITMAP_PLAYF, IDB_BITMAP_PLAYX);
- m_BtnFFwd.LoadBitmaps(IDB_BITMAP_FFWDU, IDB_BITMAP_FFWDD, IDB_BITMAP_FFWDF, IDB_BITMAP_FFWDX);
- m_BtnStop.LoadBitmaps(IDB_BITMAP_STOPU, IDB_BITMAP_STOPD, IDB_BITMAP_STOPF, IDB_BITMAP_STOPX);
- m_BtnEjct.LoadBitmaps(IDB_BITMAP_EJECTU, IDB_BITMAP_EJECTD, IDB_BITMAP_EJECTF, IDB_BITMAP_EJECTX);
- m_bBatchCapture = false;
- QueryRegistry(); // Retrieve the previous state.
- SetFileExtension(m_CaptureFilename);
- CreateDeviceMenu(); // Discover system capture devices and create appropriate menu items.
- #ifdef _DEBUG
- 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))));
- 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))));
- 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))));
- 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))));
- 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))));
- 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))));
- 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))));
- 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))));
- 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))));
- 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))));
- 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))));
- 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))));
- 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))));
- 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))));
- 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))));
- 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))));
- 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))));
- 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))));
- 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))));
- 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))));
- #endif
- // Create a source side capture graph for previewing the audio and video inputs.
- HRESULT hr = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, __uuidof(IGraphBuilder), reinterpret_cast<void**>(&m_pSourceGraph));
- if (SUCCEEDED(hr))
- {
- #ifdef _DEBUG
- hr = CDSUtils::AddGraphToRot(m_pSourceGraph, &m_ROTRegisterSource);
- #endif
- m_pIMediaEvent = m_pSourceGraph;
- ASSERT(m_pIMediaEvent);
- if (SUCCEEDED(hr))
- {
- hr = CDSUtils::AddFilter(m_pSourceGraph, CLSID_SmartTee, SMARTT_NAME, &m_pSmartT);
- if (SUCCEEDED(hr))
- {
- // Locate video screen renderer for the preview window.
- hr = CDSUtils::AddFilter(m_pSourceGraph, CLSID_VideoRendererDefault, VIDEORENDERER_NAME, &m_pVideoRenderer);
- if (SUCCEEDED(hr))
- {
- // Configure the renderer to be in windowless mode.
- CComQIPtr<ivmrfilterconfig, &iid_ivmrfilterconfig=""> pIVMRFilterConfig = m_pVideoRenderer;
- if (pIVMRFilterConfig)
- {
- hr = pIVMRFilterConfig->SetRenderingMode(VMRMode_Windowless);
- }
- // Configure the renderer to be in windowless mode.
- CComQIPtr<ivmrwindowlesscontrol, &iid_ivmrwindowlesscontrol=""> pIVMRWindowlessCtrl = m_pVideoRenderer;
- if (pIVMRWindowlessCtrl)
- {
- CWnd* pWnd = GetDlgItem(IDC_STATIC_PREVIEW);
- hr = pIVMRWindowlessCtrl->SetVideoClippingWindow(pWnd->GetSafeHwnd()); // Set the bounds of the video to the preview window.
- RECT rcDst = {0};
- pWnd->GetClientRect(&rcDst);
- SetRect(&rcDst, 0, 0, rcDst.right, rcDst.bottom);
- hr = pIVMRWindowlessCtrl->SetVideoPosition(NULL, &rcDst); // Show the whole of the source frame in the whole of the client area of the control.
- hr = pIVMRWindowlessCtrl->SetAspectRatioMode(VMR_ARMODE_LETTER_BOX); // Maintain the aspect ratio of the video.
- hr = pIVMRWindowlessCtrl->SetBorderColor(GetSysColor(COLOR_BTNFACE)); // Set the colour of the letter or pillar boxed area.
- }
- }
- // The sample grabber can be used for capturing a still during preview.
- if (SUCCEEDED(hr))
- {
- hr = CDSUtils::AddFilter(m_pSourceGraph, CLSID_SampleGrabber, SAMPLEGRABBERSTILLS_NAME, &m_pSampleGrabberStills);
- if (SUCCEEDED(hr))
- {
- // query for the sample grabber interface
- m_pISampleGrabberStills = m_pSampleGrabberStills;
- ASSERT(m_pISampleGrabberStills);
- }
- }
- // The sample grabber can be used for capturing timecode for frame accurate capture.
- if (SUCCEEDED(hr))
- {
- hr = CDSUtils::AddFilter(m_pSourceGraph, CLSID_NullRenderer, NULLRENDERER_NAME, &m_pNullRenderer);
- if (SUCCEEDED(hr))
- {
- hr = CDSUtils::AddFilter(m_pSourceGraph, CLSID_SampleGrabber, SAMPLEGRABBERTIMECODE_NAME, &m_pSampleGrabberTimecode);
- if (SUCCEEDED(hr))
- {
- // query for the sample grabber interface
- m_pISampleGrabberTimecode = m_pSampleGrabberTimecode;
- if (m_pISampleGrabberTimecode)
- {
- hr = m_pISampleGrabberTimecode->SetCallback(m_pTimecodeGrabber, 0);
- }
- }
- }
- }
- OnDeviceMenu(IDC_DEVICE_BASE + m_Device); // Set the device name and build a preview graph.
- }
- }
- }
- if (SUCCEEDED(hr))
- {
- // Create a sink side capture graph for writing the audio and video to disk.
- hr = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, __uuidof(IGraphBuilder), reinterpret_cast<void**>(&m_pSinkGraph));
- if (SUCCEEDED(hr))
- {
- #ifdef _DEBUG
- hr = CDSUtils::AddGraphToRot(m_pSinkGraph, &m_ROTRegisterSink);
- #endif
- }
- }
- return TRUE; // return TRUE unless you set the focus to a control
- }
- //-----------------------------------------------------------------------------
- // DestroyWindow
- // Called when the window is being destroyed, clean up and free all resources.
- BOOL CDecklinkIngestDlg::DestroyWindow()
- {
- #ifdef _DEBUG
- CDSUtils::RemoveGraphFromRot(m_ROTRegisterSource);
- CDSUtils::RemoveGraphFromRot(m_ROTRegisterSink);
- #endif
- // Cancel the sample grabber callbacks.
- EnableSampleGrabberCallback(false);
- if (m_pISampleGrabberTimecode)
- {
- m_pISampleGrabberTimecode->SetCallback(NULL, 0);
- }
- DestroyThreads(); // Destroy the thread which uses the source interfaces that are about to be released.
- DestroySinkGraph();
- DestroySourceGraph();
- m_RegUtils.Close();
- // Release the filter names attached to the menu item's data
- CMenu* pMenu = GetMenu();
- int Item = FindMenuItem(pMenu, TEXT("&Device"));
- if (-1 != Item)
- {
- CMenu* pSubMenu = pMenu->GetSubMenu(Item);
- int Count = pSubMenu->GetMenuItemCount();
- for (int Device=0; Device<count; ++device)="" {="" uint="" state="pSubMenu-">GetMenuState(IDC_DEVICE_BASE + Device, MF_BYCOMMAND);
- if (0xFFFFFFFF != State)
- {
- MENUITEMINFO MenuItemInfo = {0};
- MenuItemInfo.cbSize = sizeof(MenuItemInfo);
- MenuItemInfo.fMask = MIIM_DATA;
- if (pSubMenu->GetMenuItemInfo(IDC_DEVICE_BASE + Device, &MenuItemInfo))
- {
- delete [] (LPWSTR*)MenuItemInfo.dwItemData;
- }
- }
- }
- }
- int cItems = m_ClipList.GetItemCount();
- for (int item=0; item<citems; ++item)="" {="" cclip*="" pclip="reinterpret_cast<CClip*">(m_ClipList.GetItemData(item));
- if (pClip)
- {
- delete pClip;
- }
- }
- m_ClipList.DeleteAllItems();
- SAFE_DELETE(m_pTimecodeGrabber);
- SAFE_DELETE(m_pStillGrabber);
- return CDialog::DestroyWindow();
- }
- //-----------------------------------------------------------------------------
- // OnSysCommand
- //
- void CDecklinkIngestDlg::OnSysCommand(UINT nID, LPARAM lParam)
- {
- if ((nID & 0xFFF0) == IDM_ABOUTBOX)
- {
- CAboutDlg dlgAbout;
- dlgAbout.DoModal();
- }
- else
- {
- CDialog::OnSysCommand(nID, lParam);
- }
- }
- //-----------------------------------------------------------------------------
- // OnPaint
- // If you add a minimize button to your dialog, you will need the code below
- // to draw the icon. For MFC applications using the document/view model,
- // this is automatically done for you by the framework.
- void CDecklinkIngestDlg::OnPaint()
- {
- if (IsIconic())
- {
- CPaintDC dc(this); // device context for painting
- SendMessage(WM_ICONERASEBKGND, reinterpret_cast<wparam>(dc.GetSafeHdc()), 0);
- // Center icon in client rectangle
- int cxIcon = GetSystemMetrics(SM_CXICON);
- int cyIcon = GetSystemMetrics(SM_CYICON);
- CRect rect;
- GetClientRect(&rect);
- int x = (rect.Width() - cxIcon + 1) / 2;
- int y = (rect.Height() - cyIcon + 1) / 2;
- // Draw the icon
- dc.DrawIcon(x, y, m_hIcon);
- }
- else
- {
- CDialog::OnPaint();
- }
- }
- //-----------------------------------------------------------------------------
- // OnQueryDragIcon
- // The system calls this function to obtain the cursor to display while the user drags
- // the minimized window.
- HCURSOR CDecklinkIngestDlg::OnQueryDragIcon()
- {
- return static_cast<hcursor>(m_hIcon);
- }
- //-----------------------------------------------------------------------------
- // OnEditPreferences
- // Display the preferences dialog.
- void CDecklinkIngestDlg::OnFileImportEDL()
- {
- char BASED_CODE szFilters[] = "Edit Decision List|*.edl|All Files (*.*)|*.*||";
- CFileDialog FileDlg(TRUE, "Edit Decision Lists", TEXT("*.edl"), 0, szFilters, this);
- if (FileDlg.DoModal() == IDOK)
- {
- try
- {
- HRESULT hr = S_OK;
- CBaseEDLReader* pEDLReader = new CCMX3600EDLReader(FileDlg.GetPathName(), &hr);
- }
- catch (std::bad_alloc)
- {
- }
- }
- }
- //-----------------------------------------------------------------------------
- // OnEditPreferences
- // Display the preferences dialog.
- void CDecklinkIngestDlg::OnEditPreferences()
- {
- CPreferencesDlg dlg(m_CaptureFilePath, m_CaptureFilename, m_pVideoDevice, m_VideoFormat, m_pAudioDevice, m_AudioFormat, m_bMuteAudio, m_Compression);
- if (IDOK == dlg.DoModal())
- {
- // Set the correct file extension for the compression.
- m_Compression = dlg.GetCompression();
- SetFileExtension(m_CaptureFilename);
- // Validate the capture folder location and reset the file index if the location has changed.
- basic_string<tchar> Temp = dlg.GetCaptureLocation();
- if ((INVALID_FILE_ATTRIBUTES != GetFileAttributes(Temp.c_str())) && (m_CaptureFilePath != Temp))
- {
- m_CaptureFilePath = Temp;
- ResetFileIndex();
- }
- // Reset the file index if the base file name has changed.
- Temp = dlg.GetCaptureFilename();
- if (m_CaptureFilename != Temp)
- {
- m_CaptureFilename = Temp;
- ResetFileIndex();
- }
- // Set the filename for the stills grabber.
- if (m_pStillGrabber)
- {
- m_pStillGrabber->SetFilename(m_CaptureFilename);
- }
- // Rebuild the source graph if the audio or video formats or the audio preference has changed.
- CMediaType VideoFormat(dlg.GetVideoFormat());
- CMediaType AudioFormat(dlg.GetAudioFormat());
- bool bMuteAudio = dlg.GetMuteAudio();
- if (!(m_VideoFormat == VideoFormat) || !(m_AudioFormat == AudioFormat) || (m_bMuteAudio != bMuteAudio))
- {
- m_VideoFormat = VideoFormat;
- m_AudioFormat = AudioFormat;
- m_bMuteAudio = bMuteAudio;
- CreateCaptureSourceGraph();
- }
- SavePreferencesToRegistry();
- }
- }
- //-----------------------------------------------------------------------------
- // OnDeviceMenu
- // Change the device to be used for ingesting.
- void CDecklinkIngestDlg::OnDeviceMenu(UINT nID)
- {
- CMenu* pMenu = GetMenu();
- int Item = FindMenuItem(pMenu, TEXT("&Device"));
- if (-1 != Item)
- {
- // Update the menu with the new selection.
- CMenu* pSubMenu = pMenu->GetSubMenu(Item);
- pSubMenu->CheckMenuRadioItem(IDC_DEVICE_BASE, IDC_DEVICE_BASE + CDecklinkIngestDlg::MAXDEVICES - 1, nID, MF_BYCOMMAND);
- // Update the currently selected filter name.
- int Count = pSubMenu->GetMenuItemCount();
- for (int Device=0; Device<count; ++device)="" {="" uint="" state="pSubMenu-">GetMenuState(IDC_DEVICE_BASE + Device, MF_BYCOMMAND);
- if ((0xFFFFFFFF != State) && (State & MF_CHECKED))
- {
- MENUITEMINFO MenuItemInfo = {0};
- MenuItemInfo.cbSize = sizeof(MenuItemInfo);
- MenuItemInfo.fMask = MIIM_DATA;
- if (pSubMenu->GetMenuItemInfo(IDC_DEVICE_BASE + Device, &MenuItemInfo))
- {
- // Retrieve the capture device name from the menu.
- m_pDeviceNameVideo = (LPWSTR)MenuItemInfo.dwItemData;
- // Rebuild the source graph with the new device selection.
- CreateCaptureSourceGraph();
- }
- m_Device = Device;
- EXECUTE_ASSERT(ERROR_SUCCESS == m_RegUtils.SetBinary(TEXT("VideoCaptureDevice"), reinterpret_cast<lpbyte>(&m_Device), sizeof(m_Device)));
- break;
- }
- }
- }
- }
- //-----------------------------------------------------------------------------
- // OnDeviceInfo
- // Display the IO features of the selected device.
- void CDecklinkIngestDlg::OnDeviceInfo()
- {
- CDeviceInfoDlg dlg(m_pDeviceNameVideo, &CLSID_VideoInputDeviceCategory);
- dlg.DoModal();
- }
- //-----------------------------------------------------------------------------
- // OnAboutBox
- // Display the about box.
- void CDecklinkIngestDlg::OnAboutBox()
- {
- OnSysCommand(IDM_ABOUTBOX, 0);
- }
- //-----------------------------------------------------------------------------
- // OnBnClickedButtonLogclip
- // Create a file browser dialog to retrieve the name of a clip to add to the filelist.
- void CDecklinkIngestDlg::OnBnClickedButtonLogclip()
- {
- CString Tapename, Clipname, Inpoint, Outpoint;
- m_TapenameCtrl.GetWindowText(Tapename);
- m_ClipnameCtrl.GetWindowText(Clipname);
- m_InpointCtrl.GetWindowText(Inpoint);
- m_OutpointCtrl.GetWindowText(Outpoint);
- // Append an appropriate file extension.
- basic_string<tchar> clipname = Clipname;
- SetFileExtension(clipname);
- try
- {
- m_ClipList.AddItem(new CClip(Tapename.GetBuffer(), clipname.c_str(), Inpoint.GetBuffer(), Outpoint.GetBuffer(), (WORD)(UNITS / CUtils::GetAvgTimePerFrame(&m_VideoFormat))));
- if (0 != m_ClipList.GetItemCount())
- {
- EnableWindows(LogClipCtrlIDs, SIZEOF_ARRAY(LogClipCtrlIDs), TRUE);
- }
- }
- catch (std::bad_alloc)
- {
- // memory allocation error
- }
- }
- //-----------------------------------------------------------------------------
- // OnBnClickedButtonRemovefile
- // Remove the selected file from the filelist.
- void CDecklinkIngestDlg::OnBnClickedButtonRemoveclip()
- {
- m_ClipList.RemoveItem();
- // if there are no items in the list, disable the export and stop buttons
- if (0 == m_ClipList.GetItemCount())
- {
- EnableWindows(LogClipCtrlIDs, SIZEOF_ARRAY(LogClipCtrlIDs), FALSE);
- }
- }
- //-----------------------------------------------------------------------------
- // OnBnClickedButtonMovefileup
- // Move the selected file up in the filelist.
- void CDecklinkIngestDlg::OnBnClickedButtonMoveclipup()
- {
- m_ClipList.MoveItemUp();
- }
- //-----------------------------------------------------------------------------
- // OnBnClickedButtonMovefiledown
- // Move the selected file down in the filelist.
- void CDecklinkIngestDlg::OnBnClickedButtonMoveclipdown()
- {
- m_ClipList.MoveItemDown();
- }
- //-----------------------------------------------------------------------------
- // OnBnClickedButtonPlay
- //
- void CDecklinkIngestDlg::OnBnClickedButtonPlay()
- {
- if (m_pIExtTransport)
- {
- m_pIExtTransport->put_Mode(ED_MODE_PLAY);
- }
- }
- //-----------------------------------------------------------------------------
- // OnBnClickedButtonStop
- //
- void CDecklinkIngestDlg::OnBnClickedButtonStop()
- {
- if (m_pIExtTransport)
- {
- m_pIExtTransport->put_Mode(ED_MODE_STOP);
- }
- }
- //-----------------------------------------------------------------------------
- // OnBnClickedButtonFrew
- //
- void CDecklinkIngestDlg::OnBnClickedButtonFrew()
- {
- if (m_pIExtTransport)
- {
- m_pIExtTransport->put_Mode(ED_MODE_REW);
- }
- }
- //-----------------------------------------------------------------------------
- // OnBnClickedButtonFfwd
- //
- void CDecklinkIngestDlg::OnBnClickedButtonFfwd()
- {
- if (m_pIExtTransport)
- {
- m_pIExtTransport->put_Mode(ED_MODE_FF);
- }
- }
- //-----------------------------------------------------------------------------
- // OnBnClickedButtonEject
- //
- void CDecklinkIngestDlg::OnBnClickedButtonEject()
- {
- if (m_pIExtTransport)
- {
- m_pIExtTransport->put_MediaState(ED_MEDIA_UNLOAD);
- }
- }
- //-----------------------------------------------------------------------------
- // OnBnClickedButtonCaptureStill
- //
- void CDecklinkIngestDlg::OnBnClickedButtonCaptureStill()
- {
- if (m_pStillGrabber)
- {
- m_pStillGrabber->Capture();
- // Create a string for the status control.
- basic_string<tchar> Message = TEXT("Capturing still to: /"/"");
- basic_string<tchar>::size_type Index = Message.rfind(_T('/"'));
- Message.insert(Index, m_pStillGrabber->GetFilePath());
- VERIFY(m_StatusBarCtrl.SetText(Message.c_str(), 0, 0));
- }
- }
- //-----------------------------------------------------------------------------
- // OnBnClickedButtonCaptureNow
- // Start capturing from the device immediately.
- void CDecklinkIngestDlg::OnBnClickedButtonCaptureNow()
- {
- TCHAR szCaptureFilename[MAX_PATH] = {0};
- StringCchPrintf(szCaptureFilename, MAX_PATH, TEXT("%s %d"), m_CaptureFilename.c_str(), m_FileIndex++);
- if (SUCCEEDED(CreateCaptureSinkGraph(szCaptureFilename)))
- {
- // Start the capture streams.
- REFERENCE_TIME rtStop = MAXLONGLONG;
- if (SUCCEEDED(ConfigureSourceGraph(NULL, &rtStop))) // Start the streams immediately and cancel any pending stop requests.
- {
- EnableSampleGrabberCallback(false); // Cancel the sample grabber callback during capture.
- StartCapture();
- EnableWindows(CaptureCtrlIDs, SIZEOF_ARRAY(CaptureCtrlIDs), FALSE);
- CWnd* pWnd = GetDlgItem(IDC_BUTTON_ABORT);
- pWnd->EnableWindow(TRUE);
- pWnd->SetWindowText(TEXT("Stop"));
- // Create a string for the status control.
- basic_string<tchar> Message = TEXT("Capturing to: /"/"");
- basic_string<tchar>::size_type Index = Message.rfind(_T('/"'));
- Message.insert(Index, szCaptureFilename);
- VERIFY(m_StatusBarCtrl.SetText(Message.c_str(), 0, 0));
- }
- }
- }
- //-----------------------------------------------------------------------------
- // OnBnClickedButtonBatchCapture
- // Start capturing from the device using the batch list.
- void CDecklinkIngestDlg::OnBnClickedButtonBatchCapture()
- {
- CClip* pClip = reinterpret_cast<cclip*>(m_ClipList.GetItemData(0));
- basic_string<tchar> Message = TEXT("Please load the tape ");
- Message.append(pClip->Tapename());
- Message.append(TEXT(" into the deck."));
- if (IDOK == MessageBox(Message.c_str(), TEXT("Check/Load tape"), MB_ICONEXCLAMATION | MB_OKCANCEL))
- {
- m_CaptureTapename = pClip->Tapename();
- EnableSampleGrabberCallback(false); // Cancel the sample grabber callback during capture.
- EnableWindows(CaptureCtrlIDs, SIZEOF_ARRAY(CaptureCtrlIDs), FALSE);
- CWnd* pWnd = GetDlgItem(IDC_BUTTON_ABORT);
- pWnd->EnableWindow(TRUE);
- pWnd->SetWindowText(TEXT("Abort"));
- m_ClipList.SetSelectedItem(0);
- m_bBatchCapture = true;
- }
- }
- //-----------------------------------------------------------------------------
- // OnBnClickedButtonAbort
- // Stop capture.
- void CDecklinkIngestDlg::OnBnClickedButtonAbort()
- {
- m_bBatchCapture = false;
- StopCapture();
- EnableSampleGrabberCallback(true); // Enable the sample grabber callback during preview.
- EnableWindows(CaptureCtrlIDs, SIZEOF_ARRAY(CaptureCtrlIDs), TRUE);
- CWnd* pWnd = GetDlgItem(IDC_BUTTON_ABORT);
- pWnd->EnableWindow(FALSE);
- pWnd->SetWindowText(TEXT("Abort"));
- VERIFY(m_StatusBarCtrl.SetText(TEXT("Capture stopped."), 0, 0));
- m_ProgressCtrl.ShowWindow(SW_HIDE);
- }
- //-----------------------------------------------------------------------------
- // Private methods.
- //-----------------------------------------------------------------------------
- //-----------------------------------------------------------------------------
- // CreateDeviceMenu
- //
- HRESULT CDecklinkIngestDlg::CreateDeviceMenu(void)
- {
- HRESULT hr = S_OK;
- const GUID* pCategory = &CLSID_VideoInputDeviceCategory;
- list< basic_string<wchar> > DeviceList;
- CDSUtils::EnumerateDevices(pCategory, DeviceList);
- // Create a device menu item for each product present in the system.
- CMenu menuDevice;
- VERIFY(menuDevice.CreatePopupMenu());
- if (!DeviceList.empty())
- {
- int cDevices = 0;
- CComPtr<igraphbuilder> pGraph;
- if (SUCCEEDED(CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, __uuidof(IGraphBuilder), reinterpret_cast<void**>(&pGraph))))
- {
- for (list< basic_string<wchar> >::iterator it = DeviceList.begin(); it != DeviceList.end(); ++it)
- {
- IBaseFilter* pFilter = NULL;
- if (SUCCEEDED(CDSUtils::AddFilter2(pGraph, *pCategory, it->c_str(), &pFilter)))
- {
- WCHAR buf[256] = {0};
- LPWSTR pVendorInfo = NULL;
- pFilter->QueryVendorInfo(&pVendorInfo);
- if (pVendorInfo)
- {
- StringCbPrintfW(buf, sizeof(buf), L"%d. %s", cDevices + 1, pVendorInfo);
- CoTaskMemFree(reinterpret_cast<lpvoid>(pVendorInfo));
- }
- else
- {
- StringCbPrintfW(buf, sizeof(buf), L"%d. %s", cDevices + 1, it->c_str());
- }
- if (menuDevice.AppendMenu(MF_STRING, IDC_DEVICE_BASE + cDevices, CW2A(buf)))
- {
- MENUITEMINFO MenuItemInfo = {0};
- MenuItemInfo.cbSize = sizeof(MenuItemInfo);
- MenuItemInfo.fMask = MIIM_DATA;
- basic_string<wchar>::size_type len = it->length() + 1;
- LPWSTR pName = new WCHAR[len];
- ZeroMemory(pName, len);
- StringCchCopyW(pName, len, it->c_str());
- MenuItemInfo.dwItemData = (DWORD)(__int64)pName;
- menuDevice.SetMenuItemInfo(IDC_DEVICE_BASE + cDevices, &MenuItemInfo);
- }
- pGraph->RemoveFilter(pFilter);
- SAFE_RELEASE(pFilter);
- if (++cDevices >= CDecklinkIngestDlg::MAXDEVICES)
- {
- // Cannot support more than MAXDEVICES.
- break;
- }
- }
- }
- }
- if (0 < cDevices)
- {
- menuDevice.AppendMenu(MF_SEPARATOR, 0, TEXT("Separator"));
- menuDevice.AppendMenu(MF_STRING, IDC_DEVICE_INFO, TEXT("Device &Info.../tCtrl+I"));
- if (m_Device >= cDevices)
- {
- m_Device = 0;
- EXECUTE_ASSERT(ERROR_SUCCESS == m_RegUtils.SetBinary(TEXT("VideoCaptureDevice"), reinterpret_cast<lpbyte>(&m_Device), sizeof(m_Device)));
- }
- menuDevice.CheckMenuRadioItem(IDC_DEVICE_BASE, IDC_DEVICE_BASE + CDecklinkIngestDlg::MAXDEVICES - 1, IDC_DEVICE_BASE + m_Device, MF_BYCOMMAND);
- }
- else
- {
- // It was not possible to instantiate any capture devices.
- menuDevice.AppendMenu(MF_STRING | MF_GRAYED, 0, TEXT("<no devices="">"));
- hr = S_FALSE;
- }
- }
- else
- {
- // No capture devices are present in the system.
- menuDevice.AppendMenu(MF_STRING | MF_GRAYED, 0, TEXT("<no devices="">"));
- hr = S_FALSE;
- }
- // Insert the newly created device menu into the main menu.
- CMenu* pMenu = GetMenu();
- int Item = FindMenuItem(pMenu, TEXT("&Help"));
- if (-1 != Item)
- {
- hr = pMenu->InsertMenu(Item, MF_BYPOSITION | MF_POPUP | MF_STRING, (UINT_PTR)menuDevice.GetSafeHmenu(), TEXT("&Device")) ? S_OK : E_FAIL;
- }
- else
- {
- hr = E_FAIL;
- }
- menuDevice.Detach();
- return hr;
- }
- //-----------------------------------------------------------------------------
- // FindMenuItem
- //
- int CDecklinkIngestDlg::FindMenuItem(CMenu* pMenu, LPCTSTR MenuString)
- {
- int Item = -1;
- if (pMenu)
- {
- ASSERT(::IsMenu(pMenu->GetSafeHmenu()));
- int Count = pMenu->GetMenuItemCount();
- for (int item=0; item<count; ++item)="" {="" cstring="" str;="" if="" (pmenu-="">GetMenuString(item, str, MF_BYPOSITION) && (strcmp(str, CString(MenuString)) == 0))
- {
- Item = item;
- break;
- }
- }
- }
- return Item;
- }
- //-----------------------------------------------------------------------------
- // CreateThread
- //
- HRESULT CDecklinkIngestDlg::CreateThreads(void)
- {
- HRESULT hr = S_OK;
- if (NULL == m_hExitEvent)
- {
- m_hExitEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
- if (m_hExitEvent)
- {
- m_hThread = CreateThread(NULL, 0, ThreadProc, this, 0, NULL);
- if (NULL == m_hThread)
- {
- hr = AmGetLastErrorToHResult();
- }
- }
- else
- {
- hr = AmGetLastErrorToHResult();
- }
- }
- else
- {
- // Thread has already been created.
- hr = S_FALSE;
- }
- return hr;
- }
- //-----------------------------------------------------------------------------
- // DestroyThread
- //
- HRESULT CDecklinkIngestDlg::DestroyThreads(void)
- {
- HRESULT hr = S_OK;
- if (m_hExitEvent)
- {
- // signal the thread to exit
- SetEvent(m_hExitEvent);
- // wait for thread to exit
- EXECUTE_ASSERT(WAIT_OBJECT_0 == WaitForSingleObject(m_hThread, 10000));
- CloseHandle(m_hExitEvent);
- m_hExitEvent = NULL;
- CloseHandle(m_hThread);
- m_hThread = NULL;
- }
- return hr;
- }
- //-----------------------------------------------------------------------------
- // ThreadProc
- // Static function that wraps class thread implementation
- DWORD WINAPI CDecklinkIngestDlg::ThreadProc(LPVOID lpParameter)
- {
- ASSERT(lpParameter);
- CDecklinkIngestDlg* pDlg = reinterpret_cast<cdecklinkingestdlg*>(lpParameter);
- return pDlg->Thread();
- }
- //-----------------------------------------------------------------------------
- // Thread
- // Actual class thread
- DWORD CDecklinkIngestDlg::Thread(void)
- {
- HRESULT hr = S_OK;
- enum { MODE_NEWCLIP, MODE_SEEKING_TO_PREROLL, MODE_PREROLLING, MODE_CAPTURING };
- int State = MODE_NEWCLIP, CurrentItem, Preroll = 500;
- CClip* pClip = NULL;
- CTimecode Timecode;
- REFERENCE_TIME rtTimecodeStart, rtTimecodeEnd;
- static double LastRate = 0.0;
- bool bRunning = true;
- while (bRunning)
- {
- switch (WaitForSingleObject(m_hExitEvent, 5))
- {
- default:
- case WAIT_ABANDONED:
- case WAIT_OBJECT_0:
- bRunning = false;
- break;
- case WAIT_TIMEOUT:
- // Periodically check the transport.
- CheckTransport();
- if (m_pIExtTransport)
- {
- basic_string<tchar> TimecodeSource;
- if (S_OK == m_pTimecodeGrabber->GetTimecode(Timecode, &rtTimecodeStart, &rtTimecodeEnd, &TimecodeSource))
- {
- UpdateTimecodeCtrl(Timecode);
- static basic_string<tchar> LastTimecodeSource;
- if (LastTimecodeSource != TimecodeSource)
- {
- VERIFY(m_StatusBarCtrl.SetText(TimecodeSource.c_str(), 1, 0));
- LastTimecodeSource = TimecodeSource;
- }
- if (m_bBatchCapture)
- {
- switch (State)
- {
- case MODE_NEWCLIP:
- // setup a new clip, if transport is available preroll to the inpoint
- CurrentItem = m_ClipList.GetSelectedItem();
- pClip = reinterpret_cast<cclip*>(m_ClipList.GetItemData(CurrentItem));
- if (pClip)
- {
- // Build a sink side capturegraph and specify the stream start and stop times for frame accurate capture.
- hr = CreateCaptureSinkGraph(pClip->Clipname().c_str());
- if (SUCCEEDED(hr))
- {
- // update the inpoint and outpoint ctrls with the current clip
- m_InpointCtrl.SetWindowText(pClip->Inpoint().TimecodeToString());
- m_OutpointCtrl.SetWindowText(pClip->Outpoint().TimecodeToString());
- m_DurationCtrl.SetWindowText(pClip->Duration().TimecodeToString());
- if (m_CaptureTapename != pClip->Tapename())
- {
- basic_string<tchar> Message = TEXT("Please check/load the tape ");
- Message.append(pClip->Tapename());
- Message.append(TEXT(" into the deck."));
- if (IDOK == MessageBox(Message.c_str(), TEXT("Check/Load tape"), MB_ICONEXCLAMATION | MB_OKCANCEL))
- {
- m_CaptureTapename = pClip->Tapename();
- }
- else
- {
- hr = E_FAIL;
- }
- }
- }
- if (SUCCEEDED(hr))
- {
- LastRate = -1000.0;
- State = MODE_SEEKING_TO_PREROLL;
- VERIFY(m_StatusBarCtrl.SetText(TEXT("Seeking to inpoint..."), 0, 0));
- }
- else
- {
- // A serious problem occurred building the sink side capture graph.
- OnBnClickedButtonAbort();
- }
- }
- else
- {
- // No more clips.
- OnBnClickedButtonAbort();
- }
- break;
- case MODE_SEEKING_TO_PREROLL:
- // At this point the transport is seeking to the preroll inpoint,
- // wait for the deck to hit the preroll inpoint before rolling the transport.
- {
- // Calculate the 'distance' from the inpoint.
- CTimecode PreRoll(pClip->Inpoint() - Preroll), Distance;
- double Direction, Rate;
- if (PreRoll > Timecode)
- {
- Distance = PreRoll - Timecode;
- Direction = 1.0; // Move transport forwards.
- }
- else
- {
- Distance = Timecode - PreRoll;
- Direction = -1.0; // Move transport backwards.
- }
- if (Distance < 200) // 2 seconds from preroll.
- {
- // Move the transport at 1x.
- Rate = Direction;
- }
- else if (Distance < 1000) // 10 seconds from preroll.
- {
- // Move the transport at 10x.
- Rate = Direction * 10.0;
- }
- else if (Distance < 3000) // 30 seconds from preroll.
- {
- // Move the transport at 30x.
- Rate = Direction * 30.0;
- }
- else // 30+ seconds from preroll.
- {
- // Move the transport at 50x.
- Rate = Direction * 50.0;
- }
- if (LastRate != Rate)
- {
- // Change the shuttle rate of the transport.
- m_pIExtTransport->put_Rate(Rate);
- m_pIExtTransport->put_Mode(ED_MODE_SHUTTLE);
- LastRate = Rate;
- }
- if ((Distance == 0) && (1 == abs(Rate)))
- {
- // At the preroll inpoint, roll the transport and start prerolling.
- m_pIExtTransport->put_Mode(ED_MODE_PLAY);
- State = MODE_PREROLLING;
- VERIFY(m_StatusBarCtrl.SetText(TEXT("Prerolling to inpoint..."), 0, 0));
- }
- }
- break;
- case MODE_PREROLLING:
- // At this point the transport is rolling to the inpoint,
- // determine the stream time of the inpoint and set the
- // stream start times using IAMStreamControl.
- {
- CTimecode Distance = pClip->Inpoint() - Timecode;
- if (MAXLONGLONG != rtTimecodeStart)
- {
- // The timecode grabber was able to provide a stream time for the current timecode.
- // A short period before the inpoint, now that the deck is rolling, examine the stream
- // time for the timecode sample and determine the stream time of the video frame that
- // corresponds to the inpoint.
- if (Distance == 10)
- {
- REFERENCE_TIME rtAvgTimePerFrame = CUtils::GetAvgTimePerFrame(&m_VideoFormat);
- REFERENCE_TIME rtStart = rtTimecodeStart + (10 * rtAvgTimePerFrame);
- REFERENCE_TIME rtEnd = rtStart + ((pClip->Duration().GetFrameCount() - 1) * rtAvgTimePerFrame);
- ConfigureSourceGraph(&rtStart, &rtEnd); // Set the window of the stream times of samples that will cross the bridge.
- StartCapture(); // Close the bridge and unblock the capture streams.
- State = MODE_CAPTURING;
- TRACE(TEXT("CDecklinkIngestDlg::Thread() ***** INPOINT is [%10I64d %10I64d] ***** %s [%10I64d %10I64d]/r/n"), rtStart, rtEnd, Timecode.TimecodeToString(), rtTimecodeStart, rtTimecodeEnd);
- }
- }
- else
- {
- // The timecode grabber was not able to provide a stream time for the current timecode
- // so wait for the inpoint before starting capture. This is likely to suffer from
- // inaccuracies.
- if (Distance == 0)
- {
- StartCapture(); // Close the bridge and unblock the capture streams.
- State = MODE_CAPTURING;
- TRACE(TEXT("CDecklinkIngestDlg::Thread() ***** INPOINT is NOW ***** %s/r/n"), Timecode.TimecodeToString());
- }
- }
- }
- break;
- case MODE_CAPTURING:
- // Wait for the sink side capture graph to issue an EC_STREAM_CONTROL_STARTED and
- // EC_STREAM_CONTROL_STOPPED event. What the latter is received continue with the next clip.
- {
- // Provide some metrics on the capture progess.
- static CTimecode LastCaptured;
- CTimecode Captured = Timecode - pClip->Inpoint();
- CTimecode Remainder = pClip->Duration() - Captured;
- if (LastCaptured != Captured)
- {
- LastCaptured = Captured;
- basic_string<tchar> CaptureProgress = TEXT("Capturing...");
- CaptureProgress.append(Captured.TimecodeToString());
- CaptureProgress.append(TEXT(" -"));
- CaptureProgress.append(Remainder.TimecodeToString());
- VERIFY(m_StatusBarCtrl.SetText(CaptureProgress.c_str(), 0, 0));
- m_ProgressCtrl.ShowWindow(SW_SHOWNORMAL); // Show the progress control.
- m_ProgressCtrl.SetPos(Captured.GetFrameCount() * 100 / pClip->Duration().GetFrameCount());
- }
- long lEventCode, lParam1, lParam2;
- HRESULT hr = S_OK;
- while (hr = m_pIMediaEvent->GetEvent(&lEventCode, &lParam1, &lParam2, 0), SUCCEEDED(hr))
- {
- hr = m_pIMediaEvent->FreeEventParams(lEventCode, lParam1, lParam2);
- if (EC_STREAM_CONTROL_STARTED == lEventCode)
- {
- TRACE(TEXT("CDecklinkIngestDlg::Thread() - Stream started event/r/n"));
- }
- else if (EC_STREAM_CONTROL_STOPPED == lEventCode)
- {
- TRACE(TEXT("CDecklinkIngestDlg::Thread() - Stream stopped event/r/n"));
- m_pIExtTransport->put_Mode(ED_MODE_STOP); // Stop the transport.
- if (0 == m_ClipList.SetSelectedItem(++CurrentItem))
- {
- // No more clips.
- OnBnClickedButtonAbort();
- MessageBox(TEXT("Batch capture completed."), TEXT("Batch capture"), MB_ICONINFORMATION | MB_OK);
- }
- else
- {
- StopCapture(); // Open the bridge and block the capture streams.
- m_ProgressCtrl.ShowWindow(SW_HIDE); // Hide the progress control.
- }
- State = MODE_NEWCLIP;
- break;
- }
- }
- }
- break;
- }
- }
- else
- {
- if (MODE_NEWCLIP != State)
- {
- State = MODE_NEWCLIP;
- }
- }
- }
- else if (m_bBatchCapture)
- {
- // If failed to get timecode in the middle of a batch capture, abort.
- OnBnClickedButtonAbort();
- State = MODE_NEWCLIP;
- }
- }
- break;
- }
- }
- return 0;
- }
- //-----------------------------------------------------------------------------
- // CheckTransport
- // Make some rudimentary checks of the transport, such as are there comms (is
- // the RS232 connected), is it in remote mode, is the tape loaded and is record
- // inhibit disabled.
- HRESULT CDecklinkIngestDlg::CheckTransport(void)
- {
- HRESULT hr = S_FALSE;
- static int bLastEnableTimecodeCtrl = -1;
- static int bLastEnableTransportCtrls = -1;
- int bEnableTimecodeCtrl = FALSE;
- int bEnableTransportCtrls = FALSE;
- if (m_pIExtTransport)
- {
- // Test to see if there are comms with the deck.
- long state;
- hr = m_pIExtTransport->get_LocalControl(&state);
- if (SUCCEEDED(hr))
- {
- if (OAFALSE == state)
- {
- // The deck is in remote control mode.
- hr = m_pIExtTransport->get_MediaState(&state);
- if (SUCCEEDED(hr) && (ED_MEDIA_UNLOAD != state))
- {
- // Enable the controls as there are comms with the deck,
- // it is in remote mode and there is a tape loaded.
- bEnableTimecodeCtrl = bEnableTransportCtrls = TRUE;
- hr = S_OK;
- }
- }
- }
- }
- else
- {
- hr = E_FAIL;
- }
- CWnd* pWnd = NULL;
- if (bLastEnableTimecodeCtrl != bEnableTimecodeCtrl)
- {
- pWnd = GetDlgItem(IDC_STATIC_TIMECODE);
- pWnd->EnableWindow(bEnableTimecodeCtrl ? TRUE : FALSE);
- bLastEnableTimecodeCtrl = bEnableTimecodeCtrl;
- }
- if (bLastEnableTransportCtrls != bEnableTransportCtrls)
- {
- EnableWindows(TransportCtrlIDs, SIZEOF_ARRAY(TransportCtrlIDs), bEnableTransportCtrls ? TRUE : FALSE);
- bLastEnableTransportCtrls = bEnableTransportCtrls;
- }
- return hr;
- }
- //-----------------------------------------------------------------------------
- // UpdateTimecodeCtrl
- //
- void CDecklinkIngestDlg::UpdateTimecodeCtrl(CTimecode& Timecode)
- {
- static CTimecode LastTimecode;
- if (LastTimecode != Timecode)
- {
- DWORD dwStyle = m_TimecodeCtrl.GetStyle();
- if (!(dwStyle & WS_DISABLED))
- {
- m_TimecodeCtrl.SetWindowText(Timecode.TimecodeToString());
- LastTimecode = Timecode;
- }
- }
- }
- //-----------------------------------------------------------------------------
- // CreateCaptureSourceGraph
- // Build a full source side capture graph but with the capture path diabled. This
- // will preview the capture stream until a capture option is selected. The capture
- // stream will then be enabled in order to write to disk.
- //
- // The following source side graph is built:
- //
- // Decklink Video Capture (Capture) -> Smart T (Capture) -> (Input 1) GMFBridge Sink
- // Smart T (Preview) -> AVI Decompressor -> Stills Sample Grabber -> Video Renderer
- // Decklink Video Capture (~Timecode) -> Timecode Sample Grabber -> Null Renderer
- // Decklink Audio Capture (Capture) -> Smart T (Capture) -> (Input 2) GMFBridge Sink
- //
- //
- // TODO: HDV Capture, device is 'Microsoft AV/C Tape Subunit Device' with output pin 'MPEG2TS Out'
- // DV Capture, device is 'Microsoft DV Camera and VCR' with output pins 'DV Vid Out' and 'DV A/V Out'
- // NOTE: These devices can come and go so it will be necessary to keep checking that the device is available.
- //
- HRESULT CDecklinkIngestDlg::CreateCaptureSourceGraph(void)
- {
- DestroyThreads(); // Destroy the thread which uses the source interfaces that are about to be released.
- DestroySinkGraph();
- DestroySourceGraph();
- HRESULT hr = CreateThreads(), hr2 = S_OK;
- if (SUCCEEDED(hr))
- {
- // Create a bridge controller object.
- hr = CoCreateInstance(__uuidof(GMFBridgeController), NULL, CLSCTX_INPROC_SERVER, __uuidof(IGMFBridgeController), reinterpret_cast<void**>(&m_pIGMFBridgeController));
- if (SUCCEEDED(hr))
- {
- // Add two streams to the bridge controller, one for audio and one for video.
- hr = m_pIGMFBridgeController->AddStream(TRUE, eMuxInputs, TRUE);
- if (false == m_bMuteAudio)
- {
- hr2 = m_pIGMFBridgeController->AddStream(FALSE, eMuxInputs, TRUE);
- }
- if (SUCCEEDED(hr) && SUCCEEDED(hr2))
- {
- // Add a bridge sink filter for the audio and the video streams.
- CComPtr<iunknown> pIUnknown;
- hr = m_pIGMFBridgeController->InsertSinkFilter(m_pSourceGraph, &pIUnknown);
- if (SUCCEEDED(hr))
- {
- CMediaType MediaType;
- MediaType.InitMediaType();
- m_pBridgeSink = pIUnknown;
- // Add the video capture filter.
- hr = CDSUtils::AddFilter2(m_pSourceGraph, CLSID_VideoInputDeviceCategory, m_pDeviceNameVideo, &m_pVideoDevice);
- if (SUCCEEDED(hr))
- {
- m_pIExtTransport = m_pVideoDevice; // QI capture source for the transport control interface.
- m_pTimecodeGrabber->SetTimecodeFilter(m_pVideoDevice); // Set the filter for reading timecode from VITC, RP188 and RS422
- // Set the video format.
- CComPtr<iamstreamconfig> pIAMStreamConfig;
- hr = CDSUtils::FindPinInterface(m_pVideoDevice, L"Capture", __uuidof(IAMStreamConfig), reinterpret_cast<void**>(&pIAMStreamConfig));
- if (SUCCEEDED(hr))
- {
- hr = pIAMStreamConfig->SetFormat(&m_VideoFormat);
- }
- // Set the timecode format.
- pIAMStreamConfig = NULL;
- hr = CDSUtils::FindPinInterface(m_pVideoDevice, L"~Timecode", __uuidof(IAMStreamConfig), reinterpret_cast<void**>(&pIAMStreamConfig));
- if (SUCCEEDED(hr))
- {
- MediaType.SetType(&MEDIATYPE_Timecode);
- MediaType.SetSampleSize(sizeof(TIMECODE_SAMPLE));
- MediaType.SetFormatType(&FORMAT_None);
- hr = pIAMStreamConfig->SetFormat(&MediaType);
- }
- }
- // Add the audio capture filter by creating the audio capture filter name from the name of the video capture filter.
- basic_string<wchar> DeviceNameAudio = m_pDeviceNameVideo;
- basic_string<wchar>::size_type Index = DeviceNameAudio.find(L"Video");
- if (basic_string<wchar>::npos != Index)
- {
- DeviceNameAudio.replace(Index, 5, L"Audio");
- }
- hr = CDSUtils::AddFilter2(m_pSourceGraph, CLSID_AudioInputDeviceCategory, DeviceNameAudio.c_str(), &m_pAudioDevice);
- if (SUCCEEDED(hr))
- {
- // Set the audio format.
- CComPtr<iamstreamconfig> pIAMStreamConfig;
- hr = CDSUtils::FindPinInterface(m_pAudioDevice, L"Capture", __uuidof(IAMStreamConfig), reinterpret_cast<void**>(&pIAMStreamConfig));
- if (SUCCEEDED(hr))
- {
- hr = pIAMStreamConfig->SetFormat(&m_AudioFormat);
- }
- }
- if (SUCCEEDED(hr))
- {
- // Attempt to render the timecode pin.
- hr = CDSUtils::ConnectFilters(m_pSourceGraph, m_pVideoDevice, L"~Timecode", m_pSampleGrabberTimecode, NULL);
- if (SUCCEEDED(hr))
- {
- hr = CDSUtils::ConnectFilters(m_pSourceGraph, m_pSampleGrabberTimecode, NULL, m_pNullRenderer, NULL);
- }
- // Attempt to render the video preview path.
- hr = CDSUtils::ConnectFilters(m_pSourceGraph, m_pVideoDevice, L"Capture", m_pSmartT, NULL);
- if (SUCCEEDED(hr))
- {
- // Set the media type for the still sample grabber by modifying a copy of the video capture format.
- MediaType.Set(m_VideoFormat); // Copy the current video format.
- MediaType.SetSubtype(&MEDIASUBTYPE_RGB32);
- BITMAPINFOHEADER* pbmih = CUtils::GetBMIHeader(&MediaType);
- if (pbmih)
- {
- // Set to BGRA as the still grabber can only handles this pixel format.
- pbmih->biBitCount = 32;
- pbmih->biCompression = BI_RGB;
- pbmih->biSizeImage = CUtils::GetImageSize(pbmih);
- MediaType.SetSampleSize(pbmih->biSizeImage);
- m_pStillGrabber->SetBMIH(pbmih);
- if (m_pISampleGrabberStills)
- {
- hr = m_pISampleGrabberStills->SetMediaType(&MediaType);
- }
- }
- // Connect the preview pin of the smart-T to the video renderer.
- hr = CDSUtils::ConnectFilters(m_pSourceGraph, m_pSmartT, L"Preview", m_pSampleGrabberStills, NULL);
- if (SUCCEEDED(hr))
- {
- hr = CDSUtils::ConnectFilters(m_pSourceGraph, m_pSampleGrabberStills, NULL, m_pVideoRenderer, NULL);
- if (SUCCEEDED(hr))
- {
- EnableSampleGrabberCallback(true);
- }
- }
- }
- // Attempt to render the video capture path.
- if (SUCCEEDED(hr))
- {
- hr = CDSUtils::ConnectFilters(m_pSourceGraph, m_pSmartT, L"Capture", m_pBridgeSink, L"Input 1");
- }
- // Attempt to render the audio path.
- if (SUCCEEDED(hr) && (false == m_bMuteAudio))
- {
- hr = CDSUtils::ConnectFilters(m_pSourceGraph, m_pAudioDevice, L"Capture", m_pBridgeSink, NULL);
- }
- // Run the source side capture graph until a change is made that requires the graph
- // to be rebuilt, such as a different capture device is selected.
- if (SUCCEEDED(hr))
- {
- // Turn off the capture streams.
- hr = ConfigureSourceGraph(NULL, NULL);
- CComQIPtr<imediacontrol, &iid_imediacontrol=""> pIMediaControl = m_pSourceGraph;
- if (pIMediaControl)
- {
- pIMediaControl->Run();
- }
- }
- }
- }
- }
- }
- }
- return hr;
- }
- //-----------------------------------------------------------------------------
- // ConfigureSourceGraph
- // Sets the stream start and stop times on the input pins of the bridge sink filter.
- // As all pins are configured to have the same start and stop times, only one pin is
- // configured to issue EC_STREAM_CONTROL_STARTED and EC_STREAM_CONTROL_STOPPED event
- // notifications. These notifications are useful for monitoring the state of the
- // stream and once EC_STREAM_CONTROL_STOPPED is received, the file writing graph
- // can be stopped to finalise the file and complete the capture.
- HRESULT CDecklinkIngestDlg::ConfigureSourceGraph(REFERENCE_TIME* prtStart, REFERENCE_TIME* prtStop)
- {
- HRESULT hr = S_OK;
- if (m_pBridgeSink)
- {
- DWORD dwStartCookie = 0x100, dwStopCookie = 0x10000;
- // Process all the connected input pins of bridge source filter.
- CComPtr<ienumpins> pIEnumPins = NULL;
- hr = m_pBridgeSink->EnumPins(&pIEnumPins);
- if (SUCCEEDED(hr))
- {
- CComQIPtr<iamstreamcontrol, &iid_iamstreamcontrol=""> pIAMStreamControl;
- IPin* pIPin = NULL;
- while ((S_OK == pIEnumPins->Next(1, &pIPin, NULL)) && SUCCEEDED(hr))
- {
- // Determine if the pin is connected.
- IPin* pIPinTemp = NULL;
- if (SUCCEEDED(pIPin->ConnectedTo(&pIPinTemp)))
- {
- SAFE_RELEASE(pIPinTemp); // Release the outstanding reference.
- // Set the start and stop times on connected input pins and fail
- // if the connected input pin does not have a stream control interface.
- pIAMStreamControl = pIPin;
- if (pIAMStreamControl)
- {
- // Configure the stream with the given start and stop times.
- hr = pIAMStreamControl->StartAt(prtStart, dwStartCookie);
- if (SUCCEEDED(hr))
- {
- hr = pIAMStreamControl->StopAt(prtStop, FALSE, dwStopCookie);
- }
- // As all the streams will start and stop at the same time only one pin
- // needs to issue the EC_STREAM_CONTROL_STARTED and EC_STREAM_CONTROL_STOPPED
- // event notifications.
- dwStartCookie = dwStopCookie = 0;
- }
- else
- {
- hr = E_NOINTERFACE;
- }
- }
- SAFE_RELEASE(pIPin);
- }
- }
- }
- else
- {
- hr = E_POINTER;
- }
- return hr;
- }
- //-----------------------------------------------------------------------------
- // CreateCaptureSinkGraph
- // Create a sink side graph that will use the selected compression method before
- // writing the streams to disk. A new graph is created for each file and is independent
- // of the source side graph until the bridge is closed. This graph is not run until
- // capture has been initiated.
- HRESULT CDecklinkIngestDlg::CreateCaptureSinkGraph(LPCTSTR pszFilename)
- {
- HRESULT hr = S_OK;
- if (pszFilename)
- {
- hr = DestroySinkGraph();
- if (SUCCEEDED(hr))
- {
- // Add a bridge source filter.
- CComPtr<iunknown> pIUnknown;
- hr = m_pIGMFBridgeController->InsertSourceFilter(m_pBridgeSink, m_pSinkGraph, &pIUnknown);
- if (SUCCEEDED(hr))
- {
- m_pBridgeSource = pIUnknown;
- // From the compression type, add a mux and file writer where appropriate.
- CComPtr<ibasefilter> pAVIMux, pFileWriter, pStreamControlFilter;
- if (ENC_WM == m_Compression)
- {
- hr = CDSUtils::AddFilter(m_pSinkGraph, CLSID_WMAsfWriter, L"WM ASF Writer", &pFileWriter);
- pStreamControlFilter = pFileWriter;
- }
- else if ((ENC_SEQ_CIN == m_Compression) || (ENC_SEQ_DPX == m_Compression) || (ENC_SEQ_TGA == m_Compression) || (ENC_SEQ_BMP == m_Compression))
- {
- hr = CDSUtils::AddFilter(m_pSinkGraph, CLSID_DecklinkStillSequenceSinkFilter, L"Decklink Still Sequence Sink", &pFileWriter);
- pStreamControlFilter = pFileWriter;
- }
- else
- {
- hr = CDSUtils::AddFilter(m_pSinkGraph, CLSID_AviDest, L"AVI Mux", &pAVIMux);
- if (SUCCEEDED(hr))
- {
- hr = CDSUtils::AddFilter(m_pSinkGraph, CLSID_FileWriter, L"File Writer", &pFileWriter);
- }
- pStreamControlFilter = pAVIMux;
- }
- // Build a file writing graph using the appropriate compressor.
- if (SUCCEEDED(hr))
- {
- // Obtain the IFileSinkFilter interface from the file writer filter and set the capture filename.
- m_pIFileSinkFilter = pFileWriter;
- if (m_pIFileSinkFilter)
- {
- // Create the full path of the capture filename.
- TCHAR szCaptureFilePath[MAX_PATH] = {0};
- StringCchPrintf(szCaptureFilePath, MAX_PATH, TEXT("%s"), m_CaptureFilePath.c_str());
- PathAppend(szCaptureFilePath, pszFilename);
- StringCchCat(szCaptureFilePath, MAX_PATH, m_CaptureFileExtension.c_str());
- hr = m_pIFileSinkFilter->SetFileName(CT2OLE(szCaptureFilePath), NULL);
- if (SUCCEEDED(hr))
- {
- switch (m_Compression)
- {
- default:
- case ENC_NONE:
- hr = CreateUncompressedCaptureSinkGraph(pAVIMux, pFileWriter);
- break;
- case ENC_DV:
- hr = CreateDVCaptureSinkGraph(pAVIMux, pFileWriter);
- break;
- case ENC_WM:
- hr = CreateWMCaptureSinkGraph(NULL, pFileWriter);
- break;
- case ENC_MJ:
- hr = CreateMJPEGCaptureSinkGraph(pAVIMux, pFileWriter);
- break;
- case ENC_SEQ_CIN:
- case ENC_SEQ_DPX:
- case ENC_SEQ_TGA:
- case ENC_SEQ_BMP:
- hr = CreateStillSequenceCaptureSinkGraph(NULL, pFileWriter);
- break;
- }
- if (SUCCEEDED(hr))
- {
- CComQIPtr<imediacontrol, &iid_imediacontrol=""> pIMediaControl = m_pSinkGraph;
- if (pIMediaControl)
- {
- pIMediaControl->Pause();
- }
- }
- }
- }
- else
- {
- hr = E_POINTER;
- }
- }
- }
- }
- }
- else
- {
- hr = E_INVALIDARG;
- }
- if (FAILED(hr))
- {
- MessageBox(TEXT("An error was detected while building/r/nthe compression and file writing graph."), TEXT("DirectShow Error"), MB_OK | MB_ICONEXCLAMATION);
- }
- return hr;
- }
- //-----------------------------------------------------------------------------
- // CreateUncompressedCaptureSinkGraph
- // Create a graph for writing uncompressed streams to disk using the AVI mux
- // and file writer filters.
- //
- // The following sink side graph is built:
- //
- // GMFBridge Source (Output 1) -> (Input 01) AVI Mux -> File Writer
- // GMFBridge Source (Output 2) -> (Input 02) AVI Mux
- //
- HRESULT CDecklinkIngestDlg::CreateUncompressedCaptureSinkGraph(IBaseFilter* pAVIMux, IBaseFilter* pFileWriter)
- {
- HRESULT hr = S_OK;
- if (pAVIMux && pFileWriter)
- {
- // Connect the bridge video output pin to the mux.
- hr = CDSUtils::ConnectFilters(m_pSinkGraph, m_pBridgeSource, L"Output 1", pAVIMux, NULL);
- if (SUCCEEDED(hr))
- {
- // Connect the mux to the file writer.
- hr = CDSUtils::ConnectFilters(m_pSinkGraph, pAVIMux, NULL, pFileWriter, NULL);
- // Video path connected now optionally connect the audio path.
- if (false == m_bMuteAudio)
- {
- // Connect the bridge audio output pin to the mux.
- hr = CDSUtils::ConnectFilters(m_pSinkGraph, m_pBridgeSource, L"Output 2", pAVIMux, NULL);
- }
- }
- }
- else
- {
- hr = E_POINTER;
- }
- return hr;
- }
- //-----------------------------------------------------------------------------
- // CreateDVCaptureSinkGraph
- // Create a graph for writing DV compressed streams to disk using the AVI mux
- // and file writer filter. A custom field swap filter is inserted for PAL
- //
- // The following sink side graph is built:
- //
- // GMFBridge Source (Output 1) -> AVI Decompressor -> DV Video Encoder -> (Input 01) AVI Mux -> File Writer
- // GMFBridge Source (Output 2) -----------------------------------------> (Input 02) AVI Mux
- //
- HRESULT CDecklinkIngestDlg::CreateDVCaptureSinkGraph(IBaseFilter* pAVIMux, IBaseFilter* pFileWriter)
- {
- HRESULT hr = S_OK;
- BITMAPINFOHEADER* pbmih = CUtils::GetBMIHeader(m_VideoFormat); // This is required to configure the DV encoder.
- if (pAVIMux && pFileWriter && pbmih)
- {
- // Locate the DV encoder and add it to the graph.
- CComPtr<ibasefilter> pEncoder = NULL;
- HRESULT hr = CDSUtils::AddFilter(m_pSinkGraph, CLSID_DVVideoEnc, L"DV Video Encoder", &pEncoder);
- if (SUCCEEDED(hr))
- {
- // Configure the DV encoder.
- CComQIPtr<idvenc, &iid_idvenc=""> pIDV = pEncoder;
- if (pIDV)
- {
- // Located a DV compression filter so set the format.
- int VideoFormat, DVFormat, Resolution;
- hr = pIDV->get_IFormatResolution(&VideoFormat, &DVFormat, &Resolution, FALSE, NULL);
- if (SUCCEEDED(hr))
- {
- ASSERT(DVENCODERFORMAT_DVSD == DVFormat);
- ASSERT(DVENCODERRESOLUTION_720x480 == Resolution);
- if ((DVENCODERVIDEOFORMAT_NTSC == VideoFormat) && (576 == pbmih->biHeight))
- {
- // Set the encoder to PAL if it is currently NTSC.
- VideoFormat = DVENCODERVIDEOFORMAT_PAL;
- hr = pIDV->put_IFormatResolution(VideoFormat, DVFormat, Resolution, FALSE, NULL);
- }
- else if ((DVENCODERVIDEOFORMAT_PAL == VideoFormat) && (486 == pbmih->biHeight))
- {
- // Set the encoder to NTSC if it is currently PAL.
- VideoFormat = DVENCODERVIDEOFORMAT_NTSC;
- hr = pIDV->put_IFormatResolution(VideoFormat, DVFormat, Resolution, FALSE, NULL);
- }
- }
- }
- if (SUCCEEDED(hr))
- {
- // If the format is PAL, insert the Decklink field swap filter, PAL DV is the opposite field order to PAL SD.
- if (576 == pbmih->biHeight)
- {
- CComPtr<ibasefilter> pPALFieldSwap = NULL;
- hr = CDSUtils::AddFilter(m_pSinkGraph, CLSID_DecklinkFieldSwap, L"Decklink PAL Field Swap", &pPALFieldSwap);
- if (SUCCEEDED(hr))
- {
- // Connect the bridge video output pin to the PAL field swap filter.
- hr = CDSUtils::ConnectFilters(m_pSinkGraph, m_pBridgeSource, L"Output 1", pPALFieldSwap, NULL);
- if (SUCCEEDED(hr))
- {
- // Connect the field swap filter to the DV encoder.
- hr = CDSUtils::ConnectFilters(m_pSinkGraph, pPALFieldSwap, NULL, pEncoder, NULL);
- }
- }
- }
- else
- {
- // Connect the bridge video output pin to the DV Encoder.
- hr = CDSUtils::ConnectFilters(m_pSinkGraph, m_pBridgeSource, L"Output 1", pEncoder, NULL);
- }
- if (SUCCEEDED(hr))
- {
- // Connect the DV encoder output to the AVI mux.
- hr = CDSUtils::ConnectFilters(m_pSinkGraph, pEncoder, NULL, pAVIMux, NULL);
- if (SUCCEEDED(hr))
- {
- // Connect the mux to the file writer.
- hr = CDSUtils::ConnectFilters(m_pSinkGraph, pAVIMux, NULL, pFileWriter, NULL);
- if (SUCCEEDED(hr))
- {
- // Video path connected now optionally connect the audio path.
- if (false == m_bMuteAudio)
- {
- // Connect the bridge audio output pin to the mux.
- hr = CDSUtils::ConnectFilters(m_pSinkGraph, m_pBridgeSource, L"Output 2", pAVIMux, NULL);
- }
- }
- }
- }
- }
- }
- }
- else
- {
- hr = E_POINTER;
- }
- return hr;
- }
- //-----------------------------------------------------------------------------
- // CreateWMCaptureSinkGraph
- // Create a graph for writing WM compressed streams to disk using the WM ASF writer
- // filter.
- //
- // The following sink side graph is built:
- //
- // GMFBridge Source (Output 1) -> (Video Input 1) WM ASF Writer
- // GMFBridge Source (Output 2) -> (Audio Input 1) WM ASF Writer
- //
- HRESULT CDecklinkIngestDlg::CreateWMCaptureSinkGraph(IBaseFilter* pAVIMux, IBaseFilter* pFileWriter)
- {
- HRESULT hr = S_OK;
- if (pFileWriter)
- {
- hr = ConfigureWMEncoder(pFileWriter);
- if (SUCCEEDED(hr))
- {
- if (false == m_bMuteAudio)
- {
- // Connect the bridge audio output pin to the ASF writer.
- hr = CDSUtils::ConnectFilters(m_pSinkGraph, m_pBridgeSource, pFileWriter, &MEDIATYPE_Audio);
- }
- if (SUCCEEDED(hr))
- {
- // Connect the bridge video output pin to the ASF writer.
- hr = CDSUtils::ConnectFilters(m_pSinkGraph, m_pBridgeSource, pFileWriter, &MEDIATYPE_Video);
- }
- }
- }
- else
- {
- hr = E_POINTER;
- }
- return hr;
- }
- //-----------------------------------------------------------------------------
- // ConfigureWMEncoder
- // Configure the Windows Media encoder filter.
- HRESULT CDecklinkIngestDlg::ConfigureWMEncoder(IBaseFilter* pASFWriter)
- {
- HRESULT hr = S_OK;
- // Modify the video output resolution of a system profile.
- if (pASFWriter)
- {
- // Simple system profile encoding.
- CComQIPtr<iconfigasfwriter, &iid_iconfigasfwriter=""> pICW = pASFWriter;
- if (pICW)
- {
- // NOTE: You could just use the following for a default system profile
- // hr = pICW->ConfigureFilterUsingProfileGuid(WMProfile_XXX); // RE: wmsysprf.h
- // NOTE: If you want video only capture you must modify the profile to remove the audio
- // otherwise encoding will fail
- // Load a system profile and modify the resolution of the video output
- // NOTE: The scope of the encoding is enormous, this just demonstrates how to change
- // the output video resolution from 320x240 to something larger.
- // Changing the resolution affects coding performance, it is likely that the encoder will
- // start to drop frames after a while. Using WM9 codecs will probably improve performance
- // and that has been left to the reader... ;o)
- //
- // Get a profile manager.
- CComPtr<iwmprofilemanager> pIWMProfileManager = NULL;
- hr = WMCreateProfileManager(&pIWMProfileManager);
- if (SUCCEEDED(hr))
- {
- // Load a system profile to modify.
- CComPtr<iwmprofile> pIWMProfile = NULL;
- // NOTE: Any WMProfile_XXX could be used here, or create a custom profile from scratch.
- hr = pIWMProfileManager->LoadProfileByID(WMProfile_V80_FAIRVBRVideo, &pIWMProfile);
- if (SUCCEEDED(hr))
- {
- // Search the streams for the video stream and attempt to modify the video size.
- DWORD cbStreams = 0;
- hr = pIWMProfile->GetStreamCount(&cbStreams);
- if (SUCCEEDED(hr))
- {
- IWMStreamConfig* pIWMStreamConfig = NULL;
- GUID streamType = {0};
- DWORD stream;
- if (m_bMuteAudio)
- {
- // Remove the audio stream for video only capture.
- for (stream=0; stream<cbstreams; ++stream)="" {="" hr="pIWMProfile-">GetStream(stream, &pIWMStreamConfig);
- if (SUCCEEDED(hr))
- {
- hr = pIWMStreamConfig->GetStreamType(&streamType);
- if (SUCCEEDED(hr))
- {
- if (MEDIATYPE_Audio == streamType)
- {
- if (SUCCEEDED(pIWMProfile->RemoveStream(pIWMStreamConfig)))
- {
- --cbStreams;
- }
- SAFE_RELEASE(pIWMStreamConfig);
- break;
- }
- }
- }
- }
- }
- for (stream=0; stream<cbstreams; ++stream)="" {="" hr="pIWMProfile-">GetStream(stream, &pIWMStreamConfig);
- if (SUCCEEDED(hr))
- {
- hr = pIWMStreamConfig->GetStreamType(&streamType);
- if (SUCCEEDED(hr) && (MEDIATYPE_Video == streamType))
- {
- // Found the video stream.
- CComQIPtr<iwmmediaprops, &iid_iwmmediaprops=""> pIWMMediaProps = pIWMStreamConfig;
- if (pIWMMediaProps)
- {
- // Get the size of the media type.
- WM_MEDIA_TYPE* pMediaType = NULL;
- DWORD cbMediaType = 0;
- hr = pIWMMediaProps->GetMediaType(pMediaType, &cbMediaType);
- if (SUCCEEDED(hr))
- {
- pMediaType = (WM_MEDIA_TYPE*)new char [cbMediaType];
- if (pMediaType)
- {
- hr = pIWMMediaProps->GetMediaType(pMediaType, &cbMediaType);
- if (SUCCEEDED(hr))
- {
- BITMAPINFOHEADER* pbmih = NULL;
- if (WMFORMAT_VideoInfo == pMediaType->formattype)
- {
- WMVIDEOINFOHEADER* pvih = (WMVIDEOINFOHEADER*)pMediaType->pbFormat;
- pbmih = &pvih->bmiHeader;
- }
- else if (WMFORMAT_MPEG2Video == pMediaType->formattype)
- {
- WMVIDEOINFOHEADER2* pvih = (WMVIDEOINFOHEADER2*)&((WMMPEG2VIDEOINFO*)pMediaType->pbFormat)->hdr;
- pbmih = &pvih->bmiHeader;
- }
- if (pbmih)
- {
- // Modify the video dimensions, set the property, reconfigure the stream
- // and then configure the ASF writer with this modified profile.
- pbmih->biWidth = 640; // was 320;
- pbmih->biHeight = 480; // was 240;
- pbmih->biSizeImage = pbmih->biWidth * pbmih->biHeight * pbmih->biBitCount / 8; // NOTE: This calculation is not correct for all bit depths.
- hr = pIWMMediaProps->SetMediaType(pMediaType);
- if (SUCCEEDED(hr))
- {
- // Config the ASF writer filter to use this modified system profile.
- hr = pIWMProfile->ReconfigStream(pIWMStreamConfig);
- if (SUCCEEDED(hr))
- {
- hr = pICW->ConfigureFilterUsingProfile(pIWMProfile);
- }
- }
- }
- }
- delete [] (char*)pMediaType;
- }
- }
- }
- }
- SAFE_RELEASE(pIWMStreamConfig);
- }
- }
- }
- }
- }
- /*
- // Modify other ASF writer properties.
- IServiceProvider* pProvider = NULL;
- hr = pASFWriter->QueryInterface(IID_IServiceProvider, reinterpret_cast<void**>(&pProvider));
- if (SUCCEEDED(hr))
- {
- IID_IWMWriterAdvanced2* pWMWA2 = NULL;
- hr = pProvider->QueryService(IID_IID_IWMWriterAdvanced2, IID_IID_IWMWriterAdvanced2, reinterpret_cast<void**>(&pWMWA2));
- if (SUCCEEDED(hr))
- {
- // Set the deinterlace mode.
- pWMWA2->GetInputSetting(...);
- :
- :
- SAFE_RELEASE(pWMWA2);
- }
- SAFE_RELEASE(pProvider);
- }
- */
- }
- }
- else
- {
- hr = E_INVALIDARG;
- }
- return hr;
- }
- //-----------------------------------------------------------------------------
- // CreateMJPEGCaptureSinkGraph
- // Create a graph for writing MJPEG compressed streams to disk using the AVI and
- // file writer filters.
- //
- // The following sink side graph is built:
- //
- // GMFBridge Source (Output 1) -> Decklink MJPEG Compressor -> (Input 01) AVI Mux -> File Writer
- // GMFBridge Source (Output 2) ------------------------------> (Input 02) AVI Mux
- //
- HRESULT CDecklinkIngestDlg::CreateMJPEGCaptureSinkGraph(IBaseFilter* pAVIMux, IBaseFilter* pFileWriter)
- {
- HRESULT hr = S_OK;
- if (pAVIMux && pFileWriter)
- {
- // Locate the DeckLink MJPEG encoder and add it to the graph.
- CComPtr<ibasefilter> pEncoder = NULL;
- hr = CDSUtils::AddFilter(m_pSinkGraph, CLSID_DecklinkMJPEGEncoderFilter, L"Decklink MJPEG Compressor", &pEncoder);
- if (SUCCEEDED(hr))
- {
- // Configure the MJPEG encoder.
- CComQIPtr<iamvideocompression, &iid_iamvideocompression=""> pIVC = pEncoder;
- if (pIVC)
- {
- long Capabilities;
- hr = pIVC->GetInfo(0, 0, 0, 0, 0, 0, 0, &Capabilities);
- if (SUCCEEDED(hr) && (Capabilities & CompressionCaps_CanQuality))
- {
- hr = pIVC->put_Quality(0.8);
- }
- }
- if (SUCCEEDED(hr))
- {
- // Connect the bridge video output pin to the MJPEG Encoder.
- hr = CDSUtils::ConnectFilters(m_pSinkGraph, m_pBridgeSource, L"Output 1", pEncoder, NULL);
- if (SUCCEEDED(hr))
- {
- // Connect the MJPEG encoder output to the AVI mux.
- hr = CDSUtils::ConnectFilters(m_pSinkGraph, pEncoder, NULL, pAVIMux, NULL);
- if (SUCCEEDED(hr))
- {
- // Connect the mux to the file writer.
- hr = CDSUtils::ConnectFilters(m_pSinkGraph, pAVIMux, NULL, pFileWriter, NULL);
- if (SUCCEEDED(hr))
- {
- // Video path connected now optionally connect the audio path.
- if (false == m_bMuteAudio)
- {
- // Connect the bridge audio output pin to the mux.
- hr = CDSUtils::ConnectFilters(m_pSinkGraph, m_pBridgeSource, L"Output 2", pAVIMux, NULL);
- }
- }
- }
- }
- }
- }
- }
- else
- {
- hr = E_INVALIDARG;
- }
- return hr;
- }
- //-----------------------------------------------------------------------------
- // CreateStillSequenceCaptureSinkGraph
- // Create a graph for writing a sequence of stills to disk using the DeckLink
- // Still Sequence file writer filter. The audio is written into a separate WAV
- // file using the DirectShow sample WAVDest filter.
- //
- // The following sink side graph is built:
- //
- // GMFBridge Source (Output 1) -> Decklink Still Sequence Sink
- // GMFBridge Source (Output 2) -> Wav Dest -> File Writer
- //
- HRESULT CDecklinkIngestDlg::CreateStillSequenceCaptureSinkGraph(IBaseFilter* pAVIMux, IBaseFilter* pFileWriter)
- {
- HRESULT hr = S_OK;
- if (pFileWriter)
- {
- // Create a directory using the base filename of the sequence of stills to capture the still sequence into.
- LPOLESTR pszFilename;
- hr = m_pIFileSinkFilter->GetCurFile(&pszFilename, NULL);
- if (SUCCEEDED(hr))
- {
- basic_string<tchar> FilePath = COLE2T(pszFilename);
- basic_string<tchar> Filename = FilePath;
- CoTaskMemFree(pszFilename); // Release memory.
- // Create the directory name by removing the file extension from the filename.
- basic_string<tchar>::size_type Index = FilePath.rfind(_T('.'));
- if (basic_string<tchar>::npos != Index)
- {
- FilePath.erase(Index);
- }
- // Create the directory.
- BOOL ret = CreateDirectory(FilePath.c_str(), NULL);
- if (!ret && (ERROR_ALREADY_EXISTS != GetLastError()))
- {
- hr = E_FAIL;
- }
- if (SUCCEEDED(hr))
- {
- // Create the full filename including the newly created directory in the file path.
- Index = Filename.rfind(_T('//'));
- Filename = Filename.substr(Index);
- // Insert white space at the end of the filename so that the index of the capture is not used by the still sequence writer.
- Index = Filename.rfind(_T('.'));
- Filename.insert(Index, TEXT(" "));
- TCHAR szCaptureFilePath[MAX_PATH] = {0};
- StringCchPrintf(szCaptureFilePath, MAX_PATH, TEXT("%s"), FilePath.c_str());
- PathAppend(szCaptureFilePath, Filename.c_str());
- // Set the new filename.
- hr = m_pIFileSinkFilter->SetFileName(CT2OLE(szCaptureFilePath), NULL);
- if (SUCCEEDED(hr))
- {
- // Connect the bridge video output pin to the still sequence writer.
- hr = CDSUtils::ConnectFilters(m_pSinkGraph, m_pBridgeSource, L"Output 1", pFileWriter, NULL);
- }
- // Video path connected now optionally connect the audio path.
- if (SUCCEEDED(hr) && (false == m_bMuteAudio))
- {
- // Add the Microsoft DirectShow SDK WAV Dest sample filter.
- CComPtr<ibasefilter> pAudioDest;
- hr = CDSUtils::AddFilter(m_pSinkGraph, CLSID_WavDest, L"WAV Dest", &pAudioDest);
- if (SUCCEEDED(hr))
- {
- // Add a second file writer filter for the audio stream.
- CComPtr<ibasefilter> pFileWriter;
- hr = CDSUtils::AddFilter(m_pSinkGraph, CLSID_FileWriter, L"File Writer", &pFileWriter);
- if (SUCCEEDED(hr))
- {
- // Connect the bridge audio output pin to the WAV Dest filter.
- hr = CDSUtils::ConnectFilters(m_pSinkGraph, m_pBridgeSource, L"Output 2", pAudioDest, NULL);
- if (SUCCEEDED(hr))
- {
- // Connect the WAV Dest filter to the file writer filter.
- hr = CDSUtils::ConnectFilters(m_pSinkGraph, pAudioDest, NULL, pFileWriter, NULL);
- if (SUCCEEDED(hr))
- {
- // Set the name of the audio file by using the base name of the sequence of stills.
- CComQIPtr<ifilesinkfilter, &iid_ifilesinkfilter=""> pIFileSinkFilter = pFileWriter;
- if (pIFileSinkFilter)
- {
- Filename = szCaptureFilePath;
- Index = Filename.rfind(_T('.'));
- if (basic_string<tchar>::npos != Index)
- {
- Filename.erase(Index);
- }
- Filename.append(TEXT(".wav"));
- hr = pIFileSinkFilter->SetFileName(CT2OLE(Filename.c_str()), NULL);
- }
- }
- }
- }
- }
- }
- }
- }
- }
- else
- {
- hr = E_INVALIDARG;
- }
- return hr;
- }
- //-----------------------------------------------------------------------------
- // DestroySourceGraph
- // Remove all filters from the source side graph being careful to release and
- // associated resources.
- HRESULT CDecklinkIngestDlg::DestroySourceGraph(void)
- {
- HRESULT hr = S_OK;
- CComQIPtr<imediacontrol, &iid_imediacontrol=""> pIMediaControl = m_pSourceGraph;
- if (pIMediaControl)
- {
- // Stop playback.
- pIMediaControl->Stop();
- }
- // As the sources are to be removed, release the outstanding references on them.
- m_pIGMFBridgeController = NULL;
- m_pIExtTransport = (IAMExtTransport*)NULL;
- m_pVideoDevice = m_pAudioDevice = NULL;
- if (m_pSourceGraph)
- {
- CComPtr<ienumfilters> pEnum = NULL;
- hr = m_pSourceGraph->EnumFilters(&pEnum);
- if (SUCCEEDED(hr))
- {
- IBaseFilter* pFilter = NULL;
- while (S_OK == pEnum->Next(1, &pFilter, NULL))
- {
- CDSUtils::DisconnectPins(m_pSourceGraph, pFilter);
- FILTER_INFO filterInfo = {0};
- hr = pFilter->QueryFilterInfo(&filterInfo);
- if (SUCCEEDED(hr))
- {
- SAFE_RELEASE(filterInfo.pGraph);
- if ((NULL == wcsstr(filterInfo.achName, SMARTT_NAME)) && (NULL == wcsstr(filterInfo.achName, VIDEORENDERER_NAME)) &&
- (NULL == wcsstr(filterInfo.achName, SAMPLEGRABBERSTILLS_NAME)) && (NULL == wcsstr(filterInfo.achName, SAMPLEGRABBERTIMECODE_NAME)) &&
- (NULL == wcsstr(filterInfo.achName, NULLRENDERER_NAME)))
- {
- hr = m_pSourceGraph->RemoveFilter(pFilter);
- if (SUCCEEDED(hr))
- {
- hr = pEnum->Reset();
- }
- }
- }
- SAFE_RELEASE(pFilter);
- }
- }
- }
- else
- {
- hr = E_POINTER;
- }
- return hr;
- }
- //-----------------------------------------------------------------------------
- // DestroySinkGraph
- // Remove all filters from the sink side graph.
- HRESULT CDecklinkIngestDlg::DestroySinkGraph(void)
- {
- HRESULT hr = S_OK;
- CComQIPtr<imediacontrol, &iid_imediacontrol=""> pIMediaControl = m_pSinkGraph;
- if (pIMediaControl)
- {
- // Stop playback.
- pIMediaControl->Stop();
- }
- if (m_pSinkGraph)
- {
- CComPtr<ienumfilters> pEnum = NULL;
- hr = m_pSinkGraph->EnumFilters(&pEnum);
- if (SUCCEEDED(hr))
- {
- IBaseFilter* pFilter = NULL;
- while (S_OK == pEnum->Next(1, &pFilter, NULL))
- {
- CDSUtils::DisconnectPins(m_pSinkGraph, pFilter);
- FILTER_INFO filterInfo = {0};
- hr = pFilter->QueryFilterInfo(&filterInfo);
- if (SUCCEEDED(hr))
- {
- SAFE_RELEASE(filterInfo.pGraph);
- hr = m_pSinkGraph->RemoveFilter(pFilter);
- if (SUCCEEDED(hr))
- {
- hr = pEnum->Reset();
- }
- }
- SAFE_RELEASE(pFilter);
- }
- }
- }
- else
- {
- hr = E_POINTER;
- }
- return hr;
- }
- //-----------------------------------------------------------------------------
- // StartCapture
- //
- HRESULT CDecklinkIngestDlg::StartCapture(void)
- {
- HRESULT hr = S_OK;
- TRACE(TEXT("CDecklinkIngestDlg::StartCapture()/r/n"));
- CComQIPtr<imediacontrol, &iid_imediacontrol=""> pIMediaControl = m_pSinkGraph;
- if (pIMediaControl)
- {
- // Run the capture sink graph.
- hr = pIMediaControl->Run();
- TRACE(TEXT("CDecklinkIngestDlg::StartCapture() - Running sink graph./r/n"));
- // Close the bridge.
- if (m_pIGMFBridgeController)
- {
- hr = m_pIGMFBridgeController->BridgeGraphs(m_pBridgeSink, m_pBridgeSource);
- TRACE(TEXT("CDecklinkIngestDlg::StartCapture() - Closing the bridge./r/n"));
- }
- }
- return hr;
- }
- //-----------------------------------------------------------------------------
- // StopCapture
- //
- HRESULT CDecklinkIngestDlg::StopCapture(void)
- {
- HRESULT hr = S_OK;
- TRACE(TEXT("CDecklinkIngestDlg::StopCapture()/r/n"));
- // Open the bridge.
- if (m_pIGMFBridgeController)
- {
- TRACE(TEXT("CDecklinkIngestDlg::StopCapture() - Opening the bridge./r/n"));
- hr = m_pIGMFBridgeController->BridgeGraphs(NULL, NULL);
- }
- // Stop the capture sink graph so that the file writing can be finalised.
- CComQIPtr<imediacontrol, &iid_imediacontrol=""> pIMediaControl = m_pSinkGraph;
- if (pIMediaControl)
- {
- TRACE(TEXT("CDecklinkIngestDlg::StopCapture() - Stopping sink graph./r/n"));
- hr = pIMediaControl->Stop();
- }
- // Stop the capture streams.
- hr = ConfigureSourceGraph(NULL, NULL);
- return hr;
- }
- //-----------------------------------------------------------------------------
- // EnableSampleGrabberCallback
- // Enable or disable the sample grabber used for capturing image stills.
- HRESULT CDecklinkIngestDlg::EnableSampleGrabberCallback(bool bEnable)
- {
- HRESULT hr = S_OK;
- if (m_pISampleGrabberStills)
- {
- m_pISampleGrabberStills->SetCallback(bEnable ? m_pStillGrabber : NULL, 0);
- }
- else
- {
- hr = E_POINTER;
- }
- return hr;
- }
- //-----------------------------------------------------------------------------
- // EnableWindows
- // Sets the state of a group of controls.
- void CDecklinkIngestDlg::EnableWindows(const int* pCtrlIDs, int cCtrlIDs, BOOL bEnable)
- {
- if (pCtrlIDs)
- {
- for (int Ctrl=0; Ctrl<cctrlids; ++ctrl)="" {="" cwnd*="" pwnd="GetDlgItem(pCtrlIDs[Ctrl]);" if="" (pwnd)="" pwnd-="">EnableWindow(bEnable);
- }
- }
- }
- }
- //-----------------------------------------------------------------------------
- // QueryRegistry
- // retrieve previous media formats from registry
- void CDecklinkIngestDlg::QueryRegistry(void)
- {
- if (ERROR_SUCCESS == m_RegUtils.Open(TEXT("DecklinkIngestSample")))
- {
- TCHAR szBuffer[MAX_PATH];
- ZeroMemory(szBuffer, sizeof(szBuffer));
- EXECUTE_ASSERT(ERROR_SUCCESS == m_RegUtils.GetString(TEXT("CaptureFilePath"), reinterpret_cast<lpbyte>(szBuffer), sizeof(szBuffer)));
- m_CaptureFilePath = szBuffer;
- ZeroMemory(szBuffer, sizeof(szBuffer));
- EXECUTE_ASSERT(ERROR_SUCCESS == m_RegUtils.GetString(TEXT("CaptureFilename"), reinterpret_cast<lpbyte>(szBuffer), sizeof(szBuffer)));
- m_CaptureFilename = szBuffer;
- AM_MEDIA_TYPE amt;
- if (ERROR_SUCCESS == m_RegUtils.GetBinary(TEXT("VideoMediaType"), reinterpret_cast<lpbyte>(&amt), sizeof(amt)))
- {
- VIDEOINFOHEADER vih = {0};
- if (ERROR_SUCCESS == m_RegUtils.GetBinary(TEXT("VideoFormat"), reinterpret_cast<lpbyte>(&vih), sizeof(vih)))
- {
- amt.cbFormat = sizeof(vih);
- amt.pbFormat = reinterpret_cast<byte*>(&vih);
- m_VideoFormat.Set(amt);
- }
- }
- if (ERROR_SUCCESS == m_RegUtils.GetBinary(TEXT("AudioMediaType"), reinterpret_cast<lpbyte>(&amt), sizeof(amt)))
- {
- WAVEFORMATEX wfex = {0};
- if (ERROR_SUCCESS == m_RegUtils.GetBinary(TEXT("AudioFormat"), reinterpret_cast<lpbyte>(&wfex), sizeof(wfex)))
- {
- amt.cbFormat = sizeof(wfex);
- amt.pbFormat = reinterpret_cast<byte*>(&wfex);
- m_AudioFormat.Set(amt);
- }
- }
- EXECUTE_ASSERT(ERROR_SUCCESS == m_RegUtils.GetBinary(TEXT("AudioMute"), reinterpret_cast<lpbyte>(&m_bMuteAudio), sizeof(m_bMuteAudio)));
- EXECUTE_ASSERT(ERROR_SUCCESS == m_RegUtils.GetBinary(TEXT("VideoCompressor"), reinterpret_cast<lpbyte>(&m_Compression), sizeof(m_Compression)));
- EXECUTE_ASSERT(ERROR_SUCCESS == m_RegUtils.GetBinary(TEXT("VideoCaptureDevice"), reinterpret_cast<lpbyte>(&m_Device), sizeof(m_Device)));
- if (CDecklinkIngestDlg::MAXDEVICES <= m_Device)
- {
- m_Device = 0;
- }
- }
- else
- {
- // create the key and registry values
- if (ERROR_SUCCESS == m_RegUtils.Create(TEXT("DecklinkIngestSample")))
- {
- SavePreferencesToRegistry();
- EXECUTE_ASSERT(ERROR_SUCCESS == m_RegUtils.SetBinary(TEXT("VideoCaptureDevice"), reinterpret_cast<lpbyte>(&m_Device), sizeof(m_Device)));
- }
- }
- }
- //-----------------------------------------------------------------------------
- // SavePreferencesToRegistry
- // retrieve previous media formats from registry
- void CDecklinkIngestDlg::SavePreferencesToRegistry(void)
- {
- EXECUTE_ASSERT(ERROR_SUCCESS == m_RegUtils.SetString(TEXT("CaptureFilePath"), reinterpret_cast<const byte*="">(m_CaptureFilePath.c_str()), (DWORD)m_CaptureFilePath.length()));
- EXECUTE_ASSERT(ERROR_SUCCESS == m_RegUtils.SetString(TEXT("CaptureFilename"), reinterpret_cast<const byte*="">(m_CaptureFilename.c_str()), (DWORD)m_CaptureFilename.length()));
- AM_MEDIA_TYPE amt = m_VideoFormat;
- amt.cbFormat = 0;
- amt.pbFormat = NULL;
- if (ERROR_SUCCESS == m_RegUtils.SetBinary(TEXT("VideoMediaType"), reinterpret_cast<lpbyte>(&amt), sizeof(amt)))
- {
- EXECUTE_ASSERT(ERROR_SUCCESS == m_RegUtils.SetBinary(TEXT("VideoFormat"), m_VideoFormat.Format(), sizeof(VIDEOINFOHEADER)));
- }
- amt = m_AudioFormat;
- amt.cbFormat = 0;
- amt.pbFormat = NULL;
- if (ERROR_SUCCESS == m_RegUtils.SetBinary(TEXT("AudioMediaType"), reinterpret_cast<lpbyte>(&amt), sizeof(amt)))
- {
- EXECUTE_ASSERT(ERROR_SUCCESS == m_RegUtils.SetBinary(TEXT("AudioFormat"), m_AudioFormat.Format(), m_AudioFormat.FormatLength()));
- }
- EXECUTE_ASSERT(ERROR_SUCCESS == m_RegUtils.SetBinary(TEXT("AudioMute"), reinterpret_cast<lpbyte>(&m_bMuteAudio), sizeof(m_bMuteAudio)));
- EXECUTE_ASSERT(ERROR_SUCCESS == m_RegUtils.SetBinary(TEXT("VideoCompressor"), reinterpret_cast<lpbyte>(&m_Compression), sizeof(m_Compression)));
- }
- //-----------------------------------------------------------------------------
- // SetFileExtension
- //
- void CDecklinkIngestDlg::SetFileExtension(basic_string<tchar>& Filename)
- {
- basic_string<tchar>::size_type Index = Filename.rfind(_T('.'));
- if (basic_string<tchar>::npos != Index)
- {
- Filename.erase(Index);
- }
- if (ENC_WM == m_Compression)
- {
- m_CaptureFileExtension = TEXT(".wmv");
- }
- else if (ENC_SEQ_CIN == m_Compression)
- {
- m_CaptureFileExtension = TEXT(".cin");
- }
- else if (ENC_SEQ_DPX == m_Compression)
- {
- m_CaptureFileExtension = TEXT(".dpx");
- }
- else if (ENC_SEQ_TGA == m_Compression)
- {
- m_CaptureFileExtension = TEXT(".tga");
- }
- else if (ENC_SEQ_BMP == m_Compression)
- {
- m_CaptureFileExtension = TEXT(".bmp");
- }
- else
- {
- m_CaptureFileExtension = TEXT(".avi");
- }
- }
- //-----------------------------------------------------------------------------
- // CreateStatusBarControl
- //
- void CDecklinkIngestDlg::CreateStatusBarControl(void)
- {
- // Create and configure the status bar control.
- CRect Rect;
- GetWindowRect(&Rect);
- SetWindowPos(NULL, Rect.left, Rect.top, Rect.right - Rect.left, (Rect.bottom - Rect.top) + 25, 0);
- VERIFY(m_StatusBarCtrl.Create(WS_CHILD | WS_VISIBLE | CCS_BOTTOM, CRect(0, 0, 0, 0), this, IDC_STATUS_BAR));
- m_StatusBarCtrl.GetClientRect(&Rect);
- int Widths[] = { 350, -1 };
- VERIFY(m_StatusBarCtrl.SetParts(SIZEOF_ARRAY(Widths), Widths));
- // The idea is to have the progress control sandwiched between two timecodes,
- // one indicating the progess so far and the other the progress remaining.
- // First, determine the size of the text.
- CClientDC dc(&m_StatusBarCtrl);
- CFont* pOldFont = dc.SelectObject(m_StatusBarCtrl.GetFont());
- CSize LeftSide = dc.GetTextExtent(CString(TEXT("Capturing...00:00:00:00")));
- CSize Space = dc.GetTextExtent(CString(TEXT(" ")));
- dc.SelectObject(pOldFont);
- // Second, adjust the size of the progress control to fit between the timecode
- // strings indicating the current batch capture progress.
- m_StatusBarCtrl.GetRect(0, &Rect);
- Rect.DeflateRect(3, 3);
- Rect.left += LeftSide.cx;
- Rect.right = Rect.left + (51 * Space.cx);
- // Create and configure the status bar progess control.
- VERIFY(m_ProgressCtrl.Create(WS_CHILD, Rect, &m_StatusBarCtrl, IDC_STATUS_BAR_PROGRESS));
- m_ProgressCtrl.SetRange(0, 100);
- m_ProgressCtrl.SetStep(1);
DecklinkIngestDlg.cpp
