「DecklinkCaptureDlg.cpp」の編集履歴(バックアップ)一覧はこちら

DecklinkCaptureDlg.cpp」(2007/06/06 (水) 14:57:34) の最新版変更点

追加された行は緑色になります。

削除された行は赤色になります。

//----------------------------------------------------------------------------- // $Id: DecklinkCaptureDlg.cpp,v 1.9 2006/04/11 01:11:07 ivanr Exp $ // // Desc: DirectShow capture sample // // Copyright (c) Blackmagic Design 2005. All rights reserved. //----------------------------------------------------------------------------- #include "stdafx.h" #include "DecklinkCapture.h" #include "DecklinkCaptureDlg.h" #include <initguid.h> // TODO: move this to a lib #include "DecklinkSample_uuids.h" #undef lstrlenW #ifdef _DEBUG #define new DEBUG_NEW #endif #define WM_GRAPHNOTIFY WM_APP+1 // for Filter Graph event notification //----------------------------------------------------------------------------- // CAboutDlg //----------------------------------------------------------------------------- // 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: DECLARE_MESSAGE_MAP() }; CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD) { } void CAboutDlg::DoDataExchange(CDataExchange* pDX) { CDialog::DoDataExchange(pDX); } BEGIN_MESSAGE_MAP(CAboutDlg, CDialog) END_MESSAGE_MAP() //----------------------------------------------------------------------------- // CDecklinkCaptureDlg dialog //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // Constructor // CDecklinkCaptureDlg::CDecklinkCaptureDlg(CWnd* pParent /*=NULL*/) : CDialog(CDecklinkCaptureDlg::IDD, pParent) , m_pIVW(NULL) { m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME); } //----------------------------------------------------------------------------- // DoDataExchange // void CDecklinkCaptureDlg::DoDataExchange(CDataExchange* pDX) { CDialog::DoDataExchange(pDX); DDX_Control(pDX, IDC_COMBO_VIDEOFORMATS, m_videoFormatCtrl); DDX_Control(pDX, IDC_COMBO_AUDIOFORMATS, m_audioFormatCtrl); DDX_Control(pDX, IDC_STATIC_PREVIEW, m_preview); DDX_Control(pDX, IDC_EDIT_CAPTUREFILE, m_captureFileCtrl); DDX_Control(pDX, IDC_COMBO_COMPRESSION, m_compressionCtrl); DDX_Control(pDX, IDC_COMBO_VIDEODEVICE, m_videoDeviceCtrl); DDX_Control(pDX, IDC_COMBO_AUDIODEVICE, m_audioDeviceCtrl); } BEGIN_MESSAGE_MAP(CDecklinkCaptureDlg, CDialog) ON_WM_SYSCOMMAND() ON_WM_PAINT() ON_WM_QUERYDRAGICON() //}}AFX_MSG_MAP ON_CBN_SELCHANGE(IDC_COMBO_VIDEOFORMATS, OnCbnSelchangeComboVideoformats) ON_CBN_SELCHANGE(IDC_COMBO_AUDIOFORMATS, OnCbnSelchangeComboAudioformats) ON_BN_CLICKED(IDC_CHECK_AUDIOMUTE, OnBnClickedCheckAudiomute) ON_BN_CLICKED(IDC_BUTTON_BROWSE, OnBnClickedButtonBrowse) ON_BN_CLICKED(IDC_BUTTON_CAPTURE, OnBnClickedButtonCapture) ON_BN_CLICKED(IDC_BUTTON_STOP, OnBnClickedButtonStop) ON_CBN_SELCHANGE(IDC_COMBO_COMPRESSION, OnCbnSelchangeComboCompression) ON_CBN_SELCHANGE(IDC_COMBO_VIDEODEVICE, OnCbnSelchangeComboVideodevice) ON_CBN_SELCHANGE(IDC_COMBO_AUDIODEVICE, OnCbnSelchangeComboAudiodevice) END_MESSAGE_MAP() //----------------------------------------------------------------------------- // CDecklinkCaptureDlg message handlers //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // OnInitDialog // Called before the dialog is displayed, use this message handler to initialise // our app BOOL CDecklinkCaptureDlg::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 // create a basic capture graph and preview the incoming video m_pGraph = NULL; m_pVideoCapture = NULL; m_pAudioCapture = NULL; m_pVideoRenderer = NULL; m_pSmartT = NULL; m_pControl = NULL; m_pIVW = NULL; m_pMediaEvent = NULL; m_ROTRegister = 0; m_bAudioMute = FALSE; m_compressor = 0; m_bEnableCompressionCtrl = TRUE; m_captureFile = "<Select File>"; // initialise default video media type ZeroMemory(&m_vihDefault, sizeof(m_vihDefault)); m_vihDefault.AvgTimePerFrame = 333667; m_vihDefault.bmiHeader.biWidth = 720; m_vihDefault.bmiHeader.biHeight = 486; m_vihDefault.bmiHeader.biBitCount = 16; m_vihDefault.bmiHeader.biCompression = 'YVYU'; // initialise default audio media type ZeroMemory(&m_wfexDefault, sizeof(m_wfexDefault)); m_wfexDefault.nChannels = 2; // the only field of interest // retrieve last state QueryRegistry(); m_captureFileCtrl.SetWindowText(m_captureFile); EnableControls(); // create a preview graph // add the filters that will be used by all the graphs; preview, uncompressed capture, dv capture, // mpeg capture and windows media capture HRESULT hr = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, IID_IGraphBuilder, reinterpret_cast<void**>(&m_pGraph)); if (SUCCEEDED(hr)) { #ifdef _DEBUG hr = CDSUtils::AddGraphToRot(m_pGraph, &m_ROTRegister); #endif hr = m_pGraph->QueryInterface(IID_IMediaControl, reinterpret_cast<void**>(&m_pControl)); if (SUCCEEDED(hr)) { // locate the video capture devices hr = PopulateDeviceControl(&CLSID_VideoInputDeviceCategory, &m_videoDeviceCtrl); if (SUCCEEDED(hr)) { hr = PopulateDeviceControl(&CLSID_AudioInputDeviceCategory, &m_audioDeviceCtrl); if (SUCCEEDED(hr)) { PWSTR pVideoName = (PWSTR)m_videoDeviceCtrl.GetItemData(m_videoDeviceCtrl.SetCurSel(0)); PWSTR pAudioName = (PWSTR)m_audioDeviceCtrl.GetItemData(m_audioDeviceCtrl.SetCurSel(0)); if (pVideoName && pAudioName) { hr = CDSUtils::AddFilter2(m_pGraph, CLSID_VideoInputDeviceCategory, pVideoName, &m_pVideoCapture); if (SUCCEEDED(hr)) { hr = CDSUtils::AddFilter2(m_pGraph, CLSID_AudioInputDeviceCategory, pAudioName, &m_pAudioCapture); if (SUCCEEDED(hr)) { PopulateVideoControl(); // populate the video format control with the video formats of the currently selected device PopulateAudioControl(); // populate the audio format control with the audio formats of the currently selected device PopulateCompressionControl(); // locate video screen renderer for the preview window hr = CDSUtils::AddFilter(m_pGraph, CLSID_VideoRendererDefault, L"Video Renderer", &m_pVideoRenderer); if (SUCCEEDED(hr)) { hr = CreatePreviewGraph(); } } } } } } } } 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 CDecklinkCaptureDlg::DestroyWindow() { m_regUtils.Close(); #ifdef _DEBUG CDSUtils::RemoveGraphFromRot(m_ROTRegister); #endif DestroyGraph(); SAFE_RELEASE(m_pControl); // Hide Video Window and remove owner. This has to be done prior to // destroying any window that displays video/still. if (m_pIVW) { m_pIVW->put_Visible(OAFALSE); m_pIVW->put_Owner(NULL); } SAFE_RELEASE(m_pIVW); SAFE_RELEASE(m_pMediaEvent); SAFE_RELEASE(m_pVideoRenderer); SAFE_RELEASE(m_pAudioCapture); SAFE_RELEASE(m_pVideoCapture); SAFE_RELEASE(m_pGraph); // free mediatypes attached to format controls int count = m_videoFormatCtrl.GetCount(); for (int item=0; item<count; ++item) { DeleteMediaType((AM_MEDIA_TYPE*)m_videoFormatCtrl.GetItemData(item)); } count = m_audioFormatCtrl.GetCount(); for (int item=0; item<count; ++item) { DeleteMediaType((AM_MEDIA_TYPE*)m_audioFormatCtrl.GetItemData(item)); } // release the device names attached to the item's data count = m_videoDeviceCtrl.GetCount(); for (item=0; item<count; ++item) { PWSTR pName = (PWSTR)m_videoDeviceCtrl.GetItemData(item); delete [] pName; } count = m_audioDeviceCtrl.GetCount(); for (item=0; item<count; ++item) { PWSTR pName = (PWSTR)m_audioDeviceCtrl.GetItemData(item); delete [] pName; } return CDialog::DestroyWindow(); } //----------------------------------------------------------------------------- // OnSysCommand // void CDecklinkCaptureDlg::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 CDecklinkCaptureDlg::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(); } } //----------------------------------------------------------------------------- // HandleGraphEvent // At the moment we just read the event, discard it and release memory used to store it. void CDecklinkCaptureDlg::HandleGraphEvent(void) { LONG lEventCode, lEventParam1, lEventParam2; if (!m_pMediaEvent) { return; } while (SUCCEEDED(m_pMediaEvent->GetEvent(&lEventCode, reinterpret_cast<LONG_PTR *>(&lEventParam1), reinterpret_cast<LONG_PTR *>(&lEventParam2), 0))) { // just free memory associated with event m_pMediaEvent->FreeEventParams(lEventCode, lEventParam1, lEventParam2); } } //----------------------------------------------------------------------------- // WindowProc // Have to add our own message handling loop to handle events from the preview video // window and to pass Window events onto it - this is so it redraws itself correctly etc. LRESULT CDecklinkCaptureDlg::WindowProc(UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM_GRAPHNOTIFY: HandleGraphEvent(); break; } // Pass all msgs to video window. vid window exists as child of static // picture frame. This ensures video window redraws itself etc. if (m_pIVW) { m_pIVW->NotifyOwnerMessage(reinterpret_cast<LONG_PTR>(m_hWnd) /* from me */, message, wParam, lParam); } return CDialog::WindowProc(message, wParam, lParam); } //----------------------------------------------------------------------------- // OnQueryDragIcon // The system calls this function to obtain the cursor to display while the user drags // the minimized window. HCURSOR CDecklinkCaptureDlg::OnQueryDragIcon() { return static_cast<HCURSOR>(m_hIcon); } //----------------------------------------------------------------------------- // CreatePreviewGraph // Create a graph to preview the input // NOTE: There are many ways of building graphs, you could opt for the ICaptureGraphBuilder interface which would // make things are lot simpler, however it doesn't always build the most efficient graphs. HRESULT CDecklinkCaptureDlg::CreatePreviewGraph() { HRESULT hr = S_OK; if (m_pGraph) { // locate smart-T // NOTE: The smart-T appears to hold references to its upstream connections even when its input pin // is diconnected. The smart-T has to be removed from the graph in order to clear these references which // is why the filter is enumerated and added every time the preview graph is built and removed whenever // it is destroyed. ASSERT(NULL == m_pSmartT); hr = CDSUtils::AddFilter(m_pGraph, CLSID_SmartTee, L"Smart Tee", &m_pSmartT); if (SUCCEEDED(hr)) { // DV preview is slightly different to all other previews if (ENC_DV != m_compressionCtrl.GetItemData(m_compressionCtrl.GetCurSel())) { // uncompressed, mpeg and wm preview // create the following: // // Decklink Video Capture -> Smart-T -> AVI Decompressor -> Video Renderer // Decklink Audio Capture -> Default Audio Renderer // // render the preview pin on the smart-T filter // first connect the Decklink video capture pin to the smart-T hr = CDSUtils::ConnectFilters(m_pGraph, m_pVideoCapture, NULL, m_pSmartT, NULL); if (SUCCEEDED(hr)) { // now connect the preview pin of the smart-T to the video renderer hr = CDSUtils::ConnectFilters(m_pGraph, m_pSmartT, L"Preview", m_pVideoRenderer, NULL); } } else { // DV Preview // create the following: // // Decklink Video Capture -> AVI Decompressor -> Smart-T -> Colour Space Converter -> Video Renderer // Decklink Audio Capture -> Default Audio Renderer // // this is a more efficient graph than created by the ICaptureGraphBuilder2 interface // add the AVI decompressor and colour space converter filters CComPtr<IBaseFilter> pAVIDecompressor = NULL; hr = CDSUtils::AddFilter(m_pGraph, CLSID_AVIDec, L"AVI Decompressor", &pAVIDecompressor); if (SUCCEEDED(hr)) { CComPtr<IBaseFilter> pColourSpaceConverter = NULL; hr = CDSUtils::AddFilter(m_pGraph, CLSID_Colour, L"Color Space Converter", &pColourSpaceConverter); if (SUCCEEDED(hr)) { // conect the Decklink video capture pin to the AVI decompressor hr = CDSUtils::ConnectFilters(m_pGraph, m_pVideoCapture, NULL, pAVIDecompressor, NULL); if (SUCCEEDED(hr)) { // connect AVI decompressor to the smart-T hr = CDSUtils::ConnectFilters(m_pGraph, pAVIDecompressor, NULL, m_pSmartT, NULL); if (SUCCEEDED(hr)) { // connect the preview pin of the smart-T to the colour space converter hr = CDSUtils::ConnectFilters(m_pGraph, m_pSmartT, L"Preview", pColourSpaceConverter, NULL); if (SUCCEEDED(hr)) { // connect the colour space converter to the video renderer hr = CDSUtils::ConnectFilters(m_pGraph, pColourSpaceConverter, NULL, m_pVideoRenderer, NULL); } } } } } } } } else { hr = E_POINTER; } if (SUCCEEDED(hr)) { // the video path has been connected, initialise the preview window InitialiseVideoPreview(); // optionally connect the audio path if (FALSE == m_bAudioMute) { // connect the Decklink audio capture pin to the mux hr = CDSUtils::RenderFilter(m_pGraph, m_pAudioCapture, L"Capture"); } // run the graph so that we can preview the input video if (m_pControl) { hr = m_pControl->Run(); } else { hr = E_POINTER; } } return hr; } //----------------------------------------------------------------------------- // CreateCaptureGraph // Create a graph to capture the input HRESULT CDecklinkCaptureDlg::CreateCaptureGraph() { HRESULT hr = S_OK; // tack the file writer onto the preview graph if (m_pGraph && m_pControl) { // stop the graph as we are about to modify it m_pControl->Stop(); // remove the default audio renderer so the Decklink audio capture filter // can be connected to the AVI mux, we will not preview audio whilst capturing CComPtr<IPin> pIPinOutput = NULL; hr = CDSUtils::GetPin(m_pAudioCapture, L"Capture", &pIPinOutput); if (SUCCEEDED(hr)) { // to disconnect both pins must be disconnected // find the pin connected to the Decklink audio capture pin CComPtr<IPin> pIPinConnection = NULL; hr = pIPinOutput->ConnectedTo(&pIPinConnection); if (SUCCEEDED(hr)) { // disconnect the pins hr = m_pGraph->Disconnect(pIPinOutput); hr = m_pGraph->Disconnect(pIPinConnection); // get the owning filter of the downstream pin and remove it from the graph PIN_INFO pinInfo = {0}; hr = pIPinConnection->QueryPinInfo(&pinInfo); if (SUCCEEDED(hr)) { if (pinInfo.pFilter) { hr = m_pGraph->RemoveFilter(pinInfo.pFilter); pinInfo.pFilter->Release(); } } } } // retrieve the capture filename m_captureFileCtrl.GetWindowText(m_captureFile); // store filename USES_CONVERSION; WCHAR captureFile[MAX_PATH]; wcsncpy(captureFile, A2W(m_captureFile), MAX_PATH); EXECUTE_ASSERT(ERROR_SUCCESS == m_regUtils.SetString("CaptureFile", reinterpret_cast<const BYTE*>(captureFile), sizeof(captureFile))); // decide the type of capture graph to build switch (m_compressionCtrl.GetItemData(m_compressionCtrl.GetCurSel())) { default: case ENC_NONE: hr = CreateUncompressedCaptureGraph(); break; case ENC_DV: hr = CreateDVCaptureGraph(); break; case ENC_WM: hr = CreateWMCaptureGraph(); break; } if (FAILED(hr)) { // there was a problem building the capture graph, issue a message // and rebuild preview graph char buffer[128]; StringCbPrintfA(buffer, sizeof(buffer), "The error 0x%08lx was detected when creating the capture graph with the following file name:\r\n\r\n'%s'", hr, m_captureFile); MessageBox(buffer, _T("Error"), MB_ICONERROR); OnBnClickedButtonStop(); // destroy broken capture graph, build preview graph and enable controls } } else { hr = E_POINTER; } return hr; } //----------------------------------------------------------------------------- // CreateUncompressedCaptureGraph // Create an optimum uncompressed capture graph HRESULT CDecklinkCaptureDlg::CreateUncompressedCaptureGraph() { HRESULT hr = S_OK; // uncompressed capture // locate the AVI mux and file writer filters and add them to the graph CComPtr<IBaseFilter> pAVIMux = NULL; hr = CDSUtils::AddFilter(m_pGraph, CLSID_AviDest , L"AVI Mux", &pAVIMux); if (SUCCEEDED(hr)) { CComPtr<IBaseFilter> pFileWriter = NULL; hr = CDSUtils::AddFilter(m_pGraph, CLSID_FileWriter, L"File writer", &pFileWriter); if (SUCCEEDED(hr)) { // set the output filename CComQIPtr<IFileSinkFilter, &IID_IFileSinkFilter> pIFS = pFileWriter; if (pIFS) { USES_CONVERSION; // for T2W macro hr = pIFS->SetFileName(T2W(m_captureFile), NULL); if (SUCCEEDED(hr)) { // connect the smart-T capture pin to the mux hr = CDSUtils::ConnectFilters(m_pGraph, m_pSmartT, L"Capture", pAVIMux, NULL); if (SUCCEEDED(hr)) { // connect the mux to the file writer hr = CDSUtils::ConnectFilters(m_pGraph, pAVIMux, NULL, pFileWriter, NULL); if (SUCCEEDED(hr)) { // video path connected now optionally connect the audio path if (FALSE == m_bAudioMute) { // connect the Decklink audio capture pin to the mux hr = CDSUtils::ConnectFilters(m_pGraph, m_pAudioCapture, L"Capture", pAVIMux, NULL); } if (SUCCEEDED(hr)) { m_pControl->Run(); } } } } } } } return hr; } //----------------------------------------------------------------------------- // CreateDVCaptureGraph // Create an optimum DV capture graph // NOTE: that this will only work for SD HRESULT CDecklinkCaptureDlg::CreateDVCaptureGraph() { HRESULT hr = S_OK; // locate the DV encoder, AVI mux and file writer filters and add them to the graph CComPtr<IBaseFilter> pDVEncoder = NULL; hr = CDSUtils::AddFilter(m_pGraph, CLSID_DVVideoEnc, L"DV Video Encoder", &pDVEncoder); if (SUCCEEDED(hr)) { CComPtr<IBaseFilter> pAVIMux = NULL; hr = CDSUtils::AddFilter(m_pGraph, CLSID_AviDest , L"AVI Mux", &pAVIMux); if (SUCCEEDED(hr)) { CComPtr<IBaseFilter> pFileWriter = NULL; hr = CDSUtils::AddFilter(m_pGraph, CLSID_FileWriter, L"File writer", &pFileWriter); if (SUCCEEDED(hr)) { // set the output filename CComQIPtr<IFileSinkFilter, &IID_IFileSinkFilter> pIFS = pFileWriter; if (pIFS) { USES_CONVERSION; // for T2W macro hr = pIFS->SetFileName(T2W(m_captureFile), NULL); if (SUCCEEDED(hr)) { // configure the DV encoder CComQIPtr<IDVEnc, &IID_IDVEnc> pIDV = pDVEncoder; if (pIDV) { // located a DV compression filter, 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 == m_vihDefault.bmiHeader.biHeight)) { // set the encoder to PAL if its NTSC videoFormat = DVENCODERVIDEOFORMAT_PAL; hr = pIDV->put_IFormatResolution(videoFormat, dvFormat, resolution, FALSE, NULL); } else if ((DVENCODERVIDEOFORMAT_PAL == videoFormat) && (486 == m_vihDefault.bmiHeader.biHeight)) { // set the encoder to NTSC if its 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 == m_vihDefault.bmiHeader.biHeight) { CComPtr<IBaseFilter> pPALFieldSwap = NULL; hr = CDSUtils::AddFilter(m_pGraph, CLSID_DecklinkFieldSwap, L"Decklink PAL Field Swap", &pPALFieldSwap); if (SUCCEEDED(hr)) { // connect the smart-T capture pin to the PAL field swap filter hr = CDSUtils::ConnectFilters(m_pGraph, m_pSmartT, L"Capture", pPALFieldSwap, NULL); if (SUCCEEDED(hr)) { // connect the field swap filter to the DV encoder hr = CDSUtils::ConnectFilters(m_pGraph, pPALFieldSwap, NULL, pDVEncoder, NULL); } } } else { // connect the smart-T capture pin to the DV Encoder hr = CDSUtils::ConnectFilters(m_pGraph, m_pSmartT, L"Capture", pDVEncoder, NULL); } if (SUCCEEDED(hr)) { // connect the DV encoder output to the AVI mux hr = CDSUtils::ConnectFilters(m_pGraph, pDVEncoder, NULL, pAVIMux, NULL); if (SUCCEEDED(hr)) { // connect the mux to the file writer hr = CDSUtils::ConnectFilters(m_pGraph, pAVIMux, NULL, pFileWriter, NULL); if (SUCCEEDED(hr)) { // video path connected now optionally connect the audio path if (FALSE == m_bAudioMute) { // connect the Decklink audio capture pin to the mux hr = CDSUtils::ConnectFilters(m_pGraph, m_pAudioCapture, L"Capture", pAVIMux, NULL); } if (SUCCEEDED(hr)) { m_pControl->Run(); } } } } } } } } } } return hr; } //----------------------------------------------------------------------------- // CreateWMCaptureGraph // Create an optimum Windows Media capture graph HRESULT CDecklinkCaptureDlg::CreateWMCaptureGraph() { HRESULT hr = S_OK; // locate the asf writer filter and add it to the graph CComPtr<IBaseFilter> pASFWriter = NULL; hr = CDSUtils::AddFilter(m_pGraph, CLSID_WMAsfWriter, L"WM ASF Writer", &pASFWriter); if (SUCCEEDED(hr)) { // set the output filename CComQIPtr<IFileSinkFilter, &IID_IFileSinkFilter> pIFS = pASFWriter; if (pIFS) { USES_CONVERSION; // for T2W macro hr = pIFS->SetFileName(T2W(m_captureFile), NULL); if (SUCCEEDED(hr)) { hr = ConfigureWMEncoder(pASFWriter); } } if (SUCCEEDED(hr)) { if (FALSE == m_bAudioMute) { // connect the Decklink audio capture pin to the ASF writer hr = CDSUtils::ConnectFilters(m_pGraph, m_pAudioCapture, pASFWriter, &MEDIATYPE_Audio); } if (SUCCEEDED(hr)) { // connect the smart-T capture pin to the ASF writer hr = CDSUtils::ConnectFilters(m_pGraph, m_pSmartT, pASFWriter, &MEDIATYPE_Video); if (SUCCEEDED(hr)) { m_pControl->Run(); } } } } return hr; } //----------------------------------------------------------------------------- // ConfigureWMEncoder // Configure the Windows Media encoder HRESULT CDecklinkCaptureDlg::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_bAudioMute) { // 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; } //----------------------------------------------------------------------------- // DestroyGraph // Remove all intermediate filters, keep any Decklink and video render filters as // these are used by all the graphs. HRESULT CDecklinkCaptureDlg::DestroyGraph() { HRESULT hr = S_OK; if (m_pGraph && m_pControl) { m_pControl->Stop(); // release our outstanding reference on this filter so it can be removed from the graph SAFE_RELEASE(m_pSmartT); // retrieve the name of the capture device, don't remove it in this method PWSTR pNameVideoCapture = (PWSTR)m_videoDeviceCtrl.GetItemData(m_videoDeviceCtrl.GetCurSel()); PWSTR pNameAudioCapture = (PWSTR)m_audioDeviceCtrl.GetItemData(m_audioDeviceCtrl.GetCurSel()); CComPtr<IEnumFilters> pEnum = NULL; hr = m_pGraph->EnumFilters(&pEnum); if (SUCCEEDED(hr)) { IBaseFilter* pFilter = NULL; while (S_OK == pEnum->Next(1, &pFilter, NULL)) { FILTER_INFO filterInfo = {0}; hr = pFilter->QueryFilterInfo(&filterInfo); if (SUCCEEDED(hr)) { SAFE_RELEASE(filterInfo.pGraph); if ((NULL == wcsstr(filterInfo.achName, pNameVideoCapture)) && (NULL == wcsstr(filterInfo.achName, pNameAudioCapture)) && (NULL == wcsstr(filterInfo.achName, L"Video Renderer"))) { hr = m_pGraph->RemoveFilter(pFilter); if (SUCCEEDED(hr)) { hr = pEnum->Reset(); } } } SAFE_RELEASE(pFilter); } } } else { hr = E_POINTER; } return hr; } //----------------------------------------------------------------------------- // InitialiseVideoPreview // In short get the video screen renderer to draw into the picture control, which is our preview window // the following code sets this up, in addition to adding the HandleGraphEvent and WindowProc methods // read the DXSDK docos for more detailed information void CDecklinkCaptureDlg::InitialiseVideoPreview(void) { // modify the preview window if (m_pVideoRenderer) { if (NULL == m_pIVW) { if (SUCCEEDED(m_pVideoRenderer->QueryInterface(IID_IVideoWindow, reinterpret_cast<void**>(&m_pIVW)))) { // get the window to handle redraws, etc // Set msg drain of VideoWindow to point to our dialog window. The dialog's // window procedure then handles events from the VideoWindow. HRESULT hr = m_pIVW->put_MessageDrain(reinterpret_cast<OAHWND>(m_hWnd)); if (NULL == m_pMediaEvent) { // Make graph send WM_GRAPHNOTIFY when it wants our attention see "Learning // When an Event Occurs" in the DX9 documentation. hr = m_pGraph->QueryInterface(IID_IMediaEventEx, reinterpret_cast<void**>(&m_pMediaEvent)); if (SUCCEEDED(hr)) { hr = m_pMediaEvent->SetNotifyWindow(reinterpret_cast<OAHWND>(m_hWnd), WM_GRAPHNOTIFY, 0); } // object created for it. RECT rc; m_preview.GetClientRect(&rc); m_pIVW->SetWindowPosition(rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top); // VideoWindow is a child window of the bounding rect hr = m_pIVW->put_WindowStyle(WS_CHILD); hr = m_pIVW->put_Owner(reinterpret_cast<OAHWND>(m_preview.GetSafeHwnd())); hr = m_pIVW->SetWindowForeground(-1); } } } } } //----------------------------------------------------------------------------- // PopulateDeviceControl // Fill device combo box with available devices of the specified category HRESULT CDecklinkCaptureDlg::PopulateDeviceControl(const GUID* pCategory, CComboBox* pCtrl) { HRESULT hr = S_OK; if (pCategory && pCtrl) { // first enumerate the system devices for the specifed class and filter name CComPtr<ICreateDevEnum> pSysDevEnum = NULL; hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER, IID_ICreateDevEnum, reinterpret_cast<void**>(&pSysDevEnum)); if (SUCCEEDED(hr)) { CComPtr<IEnumMoniker> pEnumCat = NULL; hr = pSysDevEnum->CreateClassEnumerator(*pCategory, &pEnumCat, 0); if (S_OK == hr) { IMoniker* pMoniker = NULL; bool Loop = true; while ((S_OK == pEnumCat->Next(1, &pMoniker, NULL)) && Loop) { IPropertyBag* pPropBag = NULL; hr = pMoniker->BindToStorage(0, 0, IID_IPropertyBag, reinterpret_cast<void**>(&pPropBag)); if (SUCCEEDED(hr)) { VARIANT varName; VariantInit(&varName); hr = pPropBag->Read(L"FriendlyName", &varName, 0); if (SUCCEEDED(hr)) { size_t len = wcslen(varName.bstrVal) + 1; PWSTR pName = new WCHAR [len]; StringCchCopyW(pName, len, varName.bstrVal); CW2AEX<> buf(varName.bstrVal); pCtrl->SetItemData(pCtrl->AddString(buf), (DWORD)pName); } VariantClear(&varName); // contained within a loop, decrement the reference count SAFE_RELEASE(pPropBag); } SAFE_RELEASE(pMoniker); } } } } else { hr = E_POINTER; } return hr; } //----------------------------------------------------------------------------- // PopulateVideoControl // Fill video format combo box with supported video formats using the IAMStreamConfig // interface. HRESULT CDecklinkCaptureDlg::PopulateVideoControl() { HRESULT hr = S_OK; if (m_pVideoCapture) { // free mediatypes attached to format controls int count = m_videoFormatCtrl.GetCount(); if (count) { for (int item=0; item<count; ++item) { DeleteMediaType((AM_MEDIA_TYPE*)m_videoFormatCtrl.GetItemData(item)); } m_videoFormatCtrl.ResetContent(); } // locate the video capture pin and QI for stream control CComPtr<IAMStreamConfig> pISC = NULL; hr = CDSUtils::FindPinInterface(m_pVideoCapture, &MEDIATYPE_Video, PINDIR_OUTPUT, IID_IAMStreamConfig, reinterpret_cast<void**>(&pISC)); if (SUCCEEDED(hr)) { // loop through all the capabilities (video formats) and populate the control int count, size; hr = pISC->GetNumberOfCapabilities(&count, &size); if (SUCCEEDED(hr)) { if (sizeof(VIDEO_STREAM_CONFIG_CAPS) == size) { AM_MEDIA_TYPE* pmt = NULL; VIDEO_STREAM_CONFIG_CAPS vscc; VIDEOINFOHEADER* pvih = NULL; for (int index=0; index<count; ++index) { hr = pISC->GetStreamCaps(index, &pmt, reinterpret_cast<BYTE*>(&vscc)); if (SUCCEEDED(hr)) { char buffer[128]; WORD PixelFormat; float FrameRate; ZeroMemory(buffer, sizeof(buffer)); pvih = (VIDEOINFOHEADER*)pmt->pbFormat; char* pPixelFormatLUT[] = {"4:2:2", "4:4:4"}; if (pvih->bmiHeader.biBitCount == 16) PixelFormat = 8; else if (pvih->bmiHeader.biBitCount == 20) PixelFormat = 10; else PixelFormat = pvih->bmiHeader.biBitCount; // provide a useful description of the formats if (486 == pvih->bmiHeader.biHeight) { if (417083 == pvih->AvgTimePerFrame) { StringCbPrintfA(buffer, sizeof(buffer), "NTSC %d-bit %s (3:2 pulldown removal)", PixelFormat, pPixelFormatLUT[(30 == PixelFormat)]); } else { StringCbPrintfA(buffer, sizeof(buffer), "NTSC %d-bit %s", PixelFormat, pPixelFormatLUT[(30 == PixelFormat)]); } } else if (576 == pvih->bmiHeader.biHeight) { StringCbPrintfA(buffer, sizeof(buffer), "PAL %d-bit %s", PixelFormat, pPixelFormatLUT[(30 == PixelFormat)]); } else { char* pFrameRateFormat[] = {"%.2f", "%.0f"}; FrameRate = (float)(long)UNITS / pvih->AvgTimePerFrame; if ((720 == pvih->bmiHeader.biHeight) && (59.94 > FrameRate)) { if ((FrameRate - (int)FrameRate) > 0.01) { StringCbPrintfA(buffer, sizeof(buffer), "HD720 %.2fp %d-bit %s (Overcranked 60p)", FrameRate, PixelFormat, pPixelFormatLUT[(30 == PixelFormat)]); } else { StringCbPrintfA(buffer, sizeof(buffer), "HD720 %.0fp %d-bit %s (Overcranked 60p)", FrameRate, PixelFormat, pPixelFormatLUT[(30 == PixelFormat)]); } } else if ((720 == pvih->bmiHeader.biHeight) && (59.94 <= FrameRate)) { if ((FrameRate - (int)FrameRate) > 0.01) { StringCbPrintfA(buffer, sizeof(buffer), "HD720 %.2fp %d-bit %s", FrameRate, PixelFormat, pPixelFormatLUT[(30 == PixelFormat)]); } else { StringCbPrintfA(buffer, sizeof(buffer), "HD720 %.0fp %d-bit %s", FrameRate, PixelFormat, pPixelFormatLUT[(30 == PixelFormat)]); } } else if ((1080 == pvih->bmiHeader.biHeight) && (50.00 <= FrameRate)) { if ((FrameRate - (int)FrameRate) > 0.01) { StringCbPrintfA(buffer, sizeof(buffer), "HD1080 %.2fi %d-bit %s", FrameRate, PixelFormat, pPixelFormatLUT[(30 == PixelFormat)]); } else { StringCbPrintfA(buffer, sizeof(buffer), "HD1080 %.0fi %d-bit %s", FrameRate, PixelFormat, pPixelFormatLUT[(30 == PixelFormat)]); } } else { if ((FrameRate - (int)FrameRate) > 0.01) { StringCbPrintfA(buffer, sizeof(buffer), "HD1080 %.2fPsF %d-bit %s", FrameRate, PixelFormat, pPixelFormatLUT[(30 == PixelFormat)]); } else { StringCbPrintfA(buffer, sizeof(buffer), "HD1080 %.0fPsF %d-bit %s", FrameRate, PixelFormat, pPixelFormatLUT[(30 == PixelFormat)]); } } } // add the item description to combo box int n = m_videoFormatCtrl.AddString(buffer); // store media type pointer in item's data section m_videoFormatCtrl.SetItemData(n, (DWORD_PTR)pmt); // set default format if ((pvih->AvgTimePerFrame == m_vihDefault.AvgTimePerFrame) && (pvih->bmiHeader.biWidth == m_vihDefault.bmiHeader.biWidth) && (pvih->bmiHeader.biHeight == m_vihDefault.bmiHeader.biHeight) && (pvih->bmiHeader.biBitCount == m_vihDefault.bmiHeader.biBitCount)) { m_videoFormatCtrl.SetCurSel(n); pISC->SetFormat(pmt); } } } } else { m_videoFormatCtrl.AddString("ERROR: Unable to retrieve video formats"); } } } } else { hr = E_POINTER; } return hr; } //----------------------------------------------------------------------------- // PopulateAudioControl // Fill audio format combo box with supported audio formats using the IAMStreamConfig // interface. HRESULT CDecklinkCaptureDlg::PopulateAudioControl() { HRESULT hr = S_OK; if (m_pAudioCapture) { // free mediatypes attached to format controls int count = m_audioFormatCtrl.GetCount(); if (count) { for (int item=0; item<count; ++item) { DeleteMediaType((AM_MEDIA_TYPE*)m_audioFormatCtrl.GetItemData(item)); } m_audioFormatCtrl.ResetContent(); } // locate the audio capture pin and QI for stream control CComPtr<IAMStreamConfig> pISC = NULL; hr = CDSUtils::FindPinInterface(m_pAudioCapture, &MEDIATYPE_Audio, PINDIR_OUTPUT, IID_IAMStreamConfig, reinterpret_cast<void**>(&pISC)); if (SUCCEEDED(hr)) { // loop through all the capabilities (audio formats) and populate the control int count, size; hr = pISC->GetNumberOfCapabilities(&count, &size); if (SUCCEEDED(hr)) { if (sizeof(AUDIO_STREAM_CONFIG_CAPS) == size) { AM_MEDIA_TYPE* pmt = NULL; AUDIO_STREAM_CONFIG_CAPS ascc; WAVEFORMATEX* pwfex = NULL; for (int index=0; index<count; ++index) { hr = pISC->GetStreamCaps(index, &pmt, reinterpret_cast<BYTE*>(&ascc)); if (SUCCEEDED(hr)) { char buffer[32]; ZeroMemory(buffer, sizeof(buffer)); pwfex = (WAVEFORMATEX*)pmt->pbFormat; // provide a useful description of the formats if (1 == pwfex->nChannels) { StringCbPrintfA(buffer, sizeof(buffer), "%d channel, %2.1fkHz, %d-bit", (int)pwfex->nChannels, (float)pwfex->nSamplesPerSec / 1000, (int)pwfex->wBitsPerSample); } else { StringCbPrintfA(buffer, sizeof(buffer), "%d channels, %2.1fkHz, %d-bit", (int)pwfex->nChannels, (float)pwfex->nSamplesPerSec / 1000, (int)pwfex->wBitsPerSample); } // add the item description to combo box int n = m_audioFormatCtrl.AddString(buffer); // store media type pointer in item's data section m_audioFormatCtrl.SetItemData(n, (DWORD_PTR)pmt); // set default format if ((pwfex->wFormatTag == m_wfexDefault.wFormatTag) && (pwfex->nChannels == m_wfexDefault.nChannels) && (pwfex->nSamplesPerSec == m_wfexDefault.nSamplesPerSec) && (pwfex->nAvgBytesPerSec == m_wfexDefault.nAvgBytesPerSec)) { m_audioFormatCtrl.SetCurSel(n); pISC->SetFormat(pmt); } } } } else { m_audioFormatCtrl.AddString("ERROR: Unable to retrieve audio formats"); } } } } else { hr = E_POINTER; } return hr; } //----------------------------------------------------------------------------- // PopulateCompressionControl // Fill compression control with a selection of video compressors, locate the // encoders and add them to the combo box if they exist. HRESULT CDecklinkCaptureDlg::PopulateCompressionControl() { int n = m_compressionCtrl.AddString("Uncompressed"); m_compressionCtrl.SetItemData(n, (DWORD_PTR)ENC_NONE); // search for the DV encoder, MPEG encoder and WM encoder IBaseFilter* pFilter = NULL; HRESULT hr = CoCreateInstance(CLSID_DVVideoEnc, 0, CLSCTX_INPROC_SERVER, IID_IBaseFilter, reinterpret_cast<void**>(&pFilter)); if (SUCCEEDED(hr)) { n = m_compressionCtrl.SetCurSel(m_compressionCtrl.AddString("DV Video Encoder")); m_compressionCtrl.SetItemData(n, (DWORD_PTR)ENC_DV); SAFE_RELEASE(pFilter); } hr = CoCreateInstance(CLSID_WMAsfWriter, 0, CLSCTX_INPROC_SERVER, IID_IBaseFilter, reinterpret_cast<void**>(&pFilter)); if (SUCCEEDED(hr)) { n = m_compressionCtrl.SetCurSel(m_compressionCtrl.AddString("Windows Media Encoder")); m_compressionCtrl.SetItemData(n, (DWORD_PTR)ENC_WM); SAFE_RELEASE(pFilter); } m_compressionCtrl.SetCurSel(m_compressor); return S_OK; } //----------------------------------------------------------------------------- // OnCbnSelchangeComboVideodevice // Rebuild graph with selected capture device void CDecklinkCaptureDlg::OnCbnSelchangeComboVideodevice() { SAFE_RELEASE(m_pVideoCapture); // release our outstanding reference // remove intermediate filters, since the device selection has changed the capture device will also be removed HRESULT hr = DestroyGraph(); if (SUCCEEDED(hr)) { // rebuild graph with new capture device selection PWSTR pName = (PWSTR)m_videoDeviceCtrl.GetItemData(m_videoDeviceCtrl.GetCurSel()); if (pName) { hr = CDSUtils::AddFilter2(m_pGraph, CLSID_VideoInputDeviceCategory, pName, &m_pVideoCapture); if (SUCCEEDED(hr)) { // as the device has changed get the current operating format so that the control // and display this as the current selection CComPtr<IAMStreamConfig> pISC = NULL; hr = CDSUtils::FindPinInterface(m_pVideoCapture, &MEDIATYPE_Video, PINDIR_OUTPUT, IID_IAMStreamConfig, reinterpret_cast<void**>(&pISC)); if (SUCCEEDED(hr)) { // get the current format of the device to set the current selection of the control AM_MEDIA_TYPE* pamt = NULL; hr = pISC->GetFormat(&pamt); if (SUCCEEDED(hr)) { if (pamt->pbFormat) { m_vihDefault = *(VIDEOINFOHEADER*)pamt->pbFormat; } DeleteMediaType(pamt); } } hr = PopulateVideoControl(); // repopulate the control with formats from the new device if (SUCCEEDED(hr)) { hr = CreatePreviewGraph(); // rebuild the graph with the new device } } } else { hr = E_POINTER; } } } //----------------------------------------------------------------------------- // OnCbnSelchangeComboAudiodevice // Rebuild graph with selected capture device void CDecklinkCaptureDlg::OnCbnSelchangeComboAudiodevice() { SAFE_RELEASE(m_pAudioCapture); // release our outstanding reference // remove intermediate filters, since the device selection has changed the capture device will also be removed HRESULT hr = DestroyGraph(); if (SUCCEEDED(hr)) { PWSTR pName = (PWSTR)m_audioDeviceCtrl.GetItemData(m_audioDeviceCtrl.GetCurSel()); if (pName) { hr = CDSUtils::AddFilter2(m_pGraph, CLSID_AudioInputDeviceCategory, pName, &m_pAudioCapture); if (SUCCEEDED(hr)) { // as the device has changed get the current operating format so that the control // and display this as the current selection CComPtr<IAMStreamConfig> pISC = NULL; hr = CDSUtils::FindPinInterface(m_pAudioCapture, &MEDIATYPE_Audio, PINDIR_OUTPUT, IID_IAMStreamConfig, reinterpret_cast<void**>(&pISC)); if (SUCCEEDED(hr)) { // get the current format of the device to set the current selection of the control AM_MEDIA_TYPE* pamt = NULL; hr = pISC->GetFormat(&pamt); if (SUCCEEDED(hr)) { if (pamt->pbFormat) { m_wfexDefault = *(WAVEFORMATEX*)pamt->pbFormat; } DeleteMediaType(pamt); } } hr = PopulateAudioControl(); // repopulate the control with formats from the new device if (SUCCEEDED(hr)) { hr = CreatePreviewGraph(); // rebuild the graph with the new device } } } else { hr = E_POINTER; } } } //----------------------------------------------------------------------------- // OnCbnSelchangeComboVideoformats // Rebuild preview graph if format selection changed void CDecklinkCaptureDlg::OnCbnSelchangeComboVideoformats() { HRESULT hr = DestroyGraph(); if (SUCCEEDED(hr)) { // locate the video capture pin and QI for stream control CComPtr<IAMStreamConfig> pISC = NULL; hr = CDSUtils::FindPinInterface(m_pVideoCapture, &MEDIATYPE_Video, PINDIR_OUTPUT, IID_IAMStreamConfig, reinterpret_cast<void**>(&pISC)); if (SUCCEEDED(hr)) { // set the new media format AM_MEDIA_TYPE* pmt = (AM_MEDIA_TYPE*)m_videoFormatCtrl.GetItemData(m_videoFormatCtrl.GetCurSel()); m_vihDefault = *(VIDEOINFOHEADER*)pmt->pbFormat; ASSERT(sizeof(VIDEOINFOHEADER) <= pmt->cbFormat); hr = pISC->SetFormat(pmt); if (SUCCEEDED(hr)) { // save the new format EXECUTE_ASSERT(ERROR_SUCCESS == m_regUtils.SetBinary("VideoFormat", reinterpret_cast<const BYTE*>(&m_vihDefault), sizeof(m_vihDefault))); // update compression control, we don't have an HD compression filter so disable compression for HD formats if (576 < m_vihDefault.bmiHeader.biHeight) { m_compressor = 0; m_compressionCtrl.SetCurSel(m_compressor); // save the new state EXECUTE_ASSERT(ERROR_SUCCESS == m_regUtils.SetBinary("VideoCompressor", reinterpret_cast<const BYTE*>(&m_compressor), sizeof(m_compressor))); m_bEnableCompressionCtrl = FALSE; } else { m_bEnableCompressionCtrl = TRUE; } EnableControls(); // rebuild the graph hr = CreatePreviewGraph(); } } } } //----------------------------------------------------------------------------- // OnCbnSelchangeComboAudioformats // Rebuild preview graph if format selection changed void CDecklinkCaptureDlg::OnCbnSelchangeComboAudioformats() { HRESULT hr = DestroyGraph(); if (SUCCEEDED(hr)) { // locate the audio capture pin and QI for stream control CComPtr<IAMStreamConfig> pISC = NULL; hr = CDSUtils::FindPinInterface(m_pAudioCapture, &MEDIATYPE_Audio, PINDIR_OUTPUT, IID_IAMStreamConfig, reinterpret_cast<void**>(&pISC)); if (SUCCEEDED(hr)) { // set the new media format AM_MEDIA_TYPE* pmt = (AM_MEDIA_TYPE*)m_audioFormatCtrl.GetItemData(m_audioFormatCtrl.GetCurSel()); m_wfexDefault = *(WAVEFORMATEX*)pmt->pbFormat; ASSERT(sizeof(WAVEFORMATEX) == pmt->cbFormat); hr = pISC->SetFormat(pmt); if (SUCCEEDED(hr)) { // save the new format EXECUTE_ASSERT(ERROR_SUCCESS == m_regUtils.SetBinary("AudioFormat", reinterpret_cast<const BYTE*>(&m_wfexDefault), sizeof(m_wfexDefault))); // rebuild the graph hr = CreatePreviewGraph(); } } } } //----------------------------------------------------------------------------- // OnCbnSelchangeComboCompression // Rebuild preview graph if compression selection changed void CDecklinkCaptureDlg::OnCbnSelchangeComboCompression() { HRESULT hr = DestroyGraph(); if (SUCCEEDED(hr)) { // save the new state m_compressor = m_compressionCtrl.GetCurSel(); EXECUTE_ASSERT(ERROR_SUCCESS == m_regUtils.SetBinary("VideoCompressor", reinterpret_cast<const BYTE*>(&m_compressor), sizeof(m_compressor))); // rebuild the graph hr = CreatePreviewGraph(); } } //----------------------------------------------------------------------------- // OnBnClickedCheckAudiomute // Rebuild the capture graph to reflect the new audio setting void CDecklinkCaptureDlg::OnBnClickedCheckAudiomute() { CButton* pCheck = (CButton*)GetDlgItem(IDC_CHECK_AUDIOMUTE); if (pCheck) { m_bAudioMute = pCheck->GetState() & 0x0003; HRESULT hr = DestroyGraph(); if (SUCCEEDED(hr)) { // save the new state EXECUTE_ASSERT(ERROR_SUCCESS == m_regUtils.SetBinary("AudioMute", reinterpret_cast<const BYTE*>(&m_bAudioMute), sizeof(m_bAudioMute))); // rebuild the graph which reflects the new audio setting hr = CreatePreviewGraph(); } } } //----------------------------------------------------------------------------- // OnBnClickedButtonBrowse // Create a file open dialog to browse for a file location void CDecklinkCaptureDlg::OnBnClickedButtonBrowse() { char BASED_CODE szFilters[] = "Windows Media Files|*.avi;*.asf;*.wmv|All Files (*.*)|*.*||"; char* pExt[] = {"*.avi", "*.avi", "*.asf;*.wmv"}; CFileDialog FileDlg(TRUE, "Windows Media Files", pExt[m_compressor], 0, szFilters, this); if (FileDlg.DoModal() == IDOK) { m_captureFile = FileDlg.GetPathName(); m_captureFileCtrl.SetWindowText(m_captureFile); } } //----------------------------------------------------------------------------- // OnBnClickedButtonCapture // Create a capture graph a start capture void CDecklinkCaptureDlg::OnBnClickedButtonCapture() { HRESULT hr = CreateCaptureGraph(); if (SUCCEEDED(hr)) { if (m_pControl) { hr = m_pControl->Run(); if (SUCCEEDED(hr)) { DisableControls(); } } } } //----------------------------------------------------------------------------- // OnBnClickedButtonStop // Stop capture and revert to preview void CDecklinkCaptureDlg::OnBnClickedButtonStop() { HRESULT hr = DestroyGraph(); if (SUCCEEDED(hr)) { hr = CreatePreviewGraph(); if (SUCCEEDED(hr)) { EnableControls(); } } } //----------------------------------------------------------------------------- // EnableControls // void CDecklinkCaptureDlg::EnableControls(void) { CWnd* pWnd = GetDlgItem(IDC_COMBO_VIDEOFORMATS); pWnd->EnableWindow(TRUE); pWnd = GetDlgItem(IDC_COMBO_AUDIOFORMATS); pWnd->EnableWindow(TRUE); pWnd = GetDlgItem(IDC_CHECK_AUDIOMUTE); pWnd->EnableWindow(TRUE); pWnd = GetDlgItem(IDC_COMBO_COMPRESSION); m_bEnableCompressionCtrl = (576 < m_vihDefault.bmiHeader.biHeight) ? FALSE : TRUE; // don't have an HDV codec do disable compression control for HD formats pWnd->EnableWindow(m_bEnableCompressionCtrl); pWnd = GetDlgItem(IDC_EDIT_CAPTUREFILE); pWnd->EnableWindow(TRUE); pWnd = GetDlgItem(IDC_BUTTON_BROWSE); pWnd->EnableWindow(TRUE); pWnd = GetDlgItem(IDC_BUTTON_CAPTURE); pWnd->EnableWindow(TRUE); pWnd = GetDlgItem(IDC_BUTTON_STOP); pWnd->EnableWindow(FALSE); } //----------------------------------------------------------------------------- // DisableControls // void CDecklinkCaptureDlg::DisableControls(void) { CWnd* pWnd = GetDlgItem(IDC_COMBO_VIDEOFORMATS); pWnd->EnableWindow(FALSE); pWnd = GetDlgItem(IDC_COMBO_AUDIOFORMATS); pWnd->EnableWindow(FALSE); pWnd = GetDlgItem(IDC_CHECK_AUDIOMUTE); pWnd->EnableWindow(FALSE); pWnd = GetDlgItem(IDC_COMBO_COMPRESSION); pWnd->EnableWindow(FALSE); pWnd = GetDlgItem(IDC_EDIT_CAPTUREFILE); pWnd->EnableWindow(FALSE); pWnd = GetDlgItem(IDC_BUTTON_BROWSE); pWnd->EnableWindow(FALSE); pWnd = GetDlgItem(IDC_BUTTON_CAPTURE); pWnd->EnableWindow(FALSE); pWnd = GetDlgItem(IDC_BUTTON_STOP); pWnd->EnableWindow(TRUE); } //----------------------------------------------------------------------------- // QueryRegistry // retrieve previous media formats from registry void CDecklinkCaptureDlg::QueryRegistry(void) { if (ERROR_SUCCESS == m_regUtils.Open("DecklinkCaptureSample")) { EXECUTE_ASSERT(ERROR_SUCCESS == m_regUtils.GetBinary("VideoFormat", reinterpret_cast<LPBYTE>(&m_vihDefault), sizeof(m_vihDefault))); EXECUTE_ASSERT(ERROR_SUCCESS == m_regUtils.GetBinary("AudioFormat", reinterpret_cast<LPBYTE>(&m_wfexDefault), sizeof(m_wfexDefault))); EXECUTE_ASSERT(ERROR_SUCCESS == m_regUtils.GetBinary("AudioMute", reinterpret_cast<LPBYTE>(&m_bAudioMute), sizeof(m_bAudioMute))); EXECUTE_ASSERT(ERROR_SUCCESS == m_regUtils.GetBinary("VideoCompressor", reinterpret_cast<LPBYTE>(&m_compressor), sizeof(m_compressor))); WCHAR captureFile[MAX_PATH]; ZeroMemory(captureFile, sizeof(captureFile)); EXECUTE_ASSERT(ERROR_SUCCESS == m_regUtils.GetString("CaptureFile", reinterpret_cast<LPBYTE>(captureFile), sizeof(captureFile))); m_captureFile = captureFile; } else { // create the key and registry values if (ERROR_SUCCESS == m_regUtils.Create("DecklinkCaptureSample")) { EXECUTE_ASSERT(ERROR_SUCCESS == m_regUtils.SetBinary("VideoFormat", reinterpret_cast<const BYTE*>(&m_vihDefault), sizeof(m_vihDefault))); EXECUTE_ASSERT(ERROR_SUCCESS == m_regUtils.SetBinary("AudioFormat", reinterpret_cast<const BYTE*>(&m_wfexDefault), sizeof(m_wfexDefault))); EXECUTE_ASSERT(ERROR_SUCCESS == m_regUtils.SetBinary("AudioMute", reinterpret_cast<const BYTE*>(&m_bAudioMute), sizeof(m_bAudioMute))); EXECUTE_ASSERT(ERROR_SUCCESS == m_regUtils.SetBinary("VideoCompressor", reinterpret_cast<const BYTE*>(&m_compressor), sizeof(m_compressor))); } } // update mute audio check box control CButton* pButton = (CButton*)GetDlgItem(IDC_CHECK_AUDIOMUTE); pButton->SetCheck(m_bAudioMute); }

表示オプション

横に並べて表示:
変化行の前後のみ表示: